some more work on plugin manager
[vuplus_dvbapp] / 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.ScrollLabel import ScrollLabel
12 from Components.Pixmap import Pixmap
13 from Components.MenuList import MenuList
14 from Components.Sources.List import List
15 from Components.Slider import Slider
16 from Components.Harddisk import harddiskmanager
17 from Components.config import config,getConfigListEntry, ConfigSubsection, ConfigText, ConfigLocations
18 from Components.Console import Console
19 from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
20 from Components.SelectionList import SelectionList
21 from Components.PluginComponent import plugins
22 from Components.About import about
23 from Components.DreamInfoHandler import DreamInfoHandler
24 from Components.Language import language
25 from Components.AVSwitch import AVSwitch
26 from Tools.Directories import pathExists, fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE, SCOPE_METADIR
27 from Tools.LoadPixmap import LoadPixmap
28 from enigma import eTimer, quitMainloop, RT_HALIGN_LEFT, RT_VALIGN_CENTER, eListboxPythonMultiContent, eListbox, gFont, getDesktop, ePicLoad
29 from cPickle import dump, load
30 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
31 from time import time, gmtime, strftime, localtime
32 from stat import ST_MTIME
33 from datetime import date
34 from twisted.web import client
35 from twisted.internet import reactor
36
37 from ImageWizard import ImageWizard
38 from BackupRestore import BackupSelection, RestoreMenu, BackupScreen, RestoreScreen, getBackupPath, getBackupFilename
39
40 config.plugins.configurationbackup = ConfigSubsection()
41 config.plugins.configurationbackup.backuplocation = ConfigText(default = '/media/hdd/', visible_width = 50, fixed_size = False)
42 config.plugins.configurationbackup.backupdirs = ConfigLocations(default=['/etc/enigma2/', '/etc/network/interfaces', '/etc/wpa_supplicant.conf', '/etc/resolv.conf', '/etc/default_gw', '/etc/hostname'])
43
44 def write_cache(cache_file, cache_data):
45         #Does a cPickle dump
46         if not os_path.isdir( os_path.dirname(cache_file) ):
47                 try:
48                         mkdir( os_path.dirname(cache_file) )
49                 except OSError:
50                             print os_path.dirname(cache_file), 'is a file'
51         fd = open(cache_file, 'w')
52         dump(cache_data, fd, -1)
53         fd.close()
54
55 def valid_cache(cache_file, cache_ttl):
56         #See if the cache file exists and is still living
57         try:
58                 mtime = stat(cache_file)[ST_MTIME]
59         except:
60                 return 0
61         curr_time = time()
62         if (curr_time - mtime) > cache_ttl:
63                 return 0
64         else:
65                 return 1
66
67 def load_cache(cache_file):
68         #Does a cPickle load
69         fd = open(cache_file)
70         cache_data = load(fd)
71         fd.close()
72         return cache_data
73
74
75 class UpdatePluginMenu(Screen):
76         skin = """
77                 <screen name="UpdatePluginMenu" position="80,130" size="560,330" title="Softwaremanager..." >
78                         <ePixmap pixmap="skin_default/border_menu_300.png" position="5,10" zPosition="1" size="300,300" transparent="1" alphatest="on" />
79                         <widget source="menu" render="Listbox" position="10,20" size="290,260" scrollbarMode="showOnDemand">
80                                 <convert type="TemplatedMultiContent">
81                                         {"template": [
82                                                         MultiContentEntryText(pos = (2, 2), size = (290, 22), flags = RT_HALIGN_LEFT, text = 1), # index 0 is the MenuText,
83                                                 ],
84                                         "fonts": [gFont("Regular", 20)],
85                                         "itemHeight": 25
86                                         }
87                                 </convert>
88                         </widget>
89                         <widget source="menu" render="Listbox" position="310,10" size="240,300" scrollbarMode="showNever" selectionDisabled="1">
90                                 <convert type="TemplatedMultiContent">
91                                         {"template": [
92                                                         MultiContentEntryText(pos = (2, 2), size = (240, 300), flags = RT_HALIGN_CENTER|RT_VALIGN_CENTER|RT_WRAP, text = 2), # index 2 is the Description,
93                                                 ],
94                                         "fonts": [gFont("Regular", 20)],
95                                         "itemHeight": 300
96                                         }
97                                 </convert>
98                         </widget>
99                 </screen>"""
100                 
101         def __init__(self, session, args = 0):
102                 Screen.__init__(self, session)
103                 self.skin_path = plugin_path
104                 self.menu = args
105                 self.list = []
106                 self.oktext = _("\nPress OK on your remote control to continue.")
107                 self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value )
108                 if self.menu == 0:
109                         self.list.append(("software-update", _("Software update"), _("\nOnline update of your Dreambox software." ) + self.oktext) )
110                         #self.list.append(("install-plugins", _("Install extensions"), _("\nInstall new Extensions or Plugins to your dreambox" ) + self.oktext) )
111                         self.list.append(("software-restore", _("Software restore"), _("\nRestore your Dreambox with a new firmware." ) + self.oktext))
112                         self.list.append(("system-backup", _("Backup system settings"), _("\nBackup your Dreambox settings." ) + self.oktext))
113                         self.list.append(("system-restore",_("Restore system settings"), _("\nRestore your Dreambox settings." ) + self.oktext))
114                         self.list.append(("ipkg-install", _("Install local IPKG"),  _("\nScan for local packages and install them." ) + self.oktext))
115                         if config.usage.setup_level.index >= 2: # expert+
116                                 self.list.append(("advanced", _("Advanced Options"), _("\nAdvanced options and settings." ) + self.oktext))
117                 elif self.menu == 1:
118                         self.list.append(("advancedrestore", _("Advanced restore"), _("\nRestore your backups by date." ) + self.oktext))
119                         self.list.append(("backuplocation", _("Choose backup location"),  _("\nSelect your backup device.\nCurrent device: " ) + config.plugins.configurationbackup.backuplocation.value + self.oktext ))
120                         self.list.append(("backupfiles", _("Choose backup files"),  _("Select files for backup. Currently selected:\n" ) + self.backupdirs + self.oktext))
121                         if config.usage.setup_level.index >= 2: # expert+
122                                 self.list.append(("ipkg-manager", _("Packet management"),  _("\nView, install and remove available or installed packages." ) + self.oktext))
123                         self.list.append(("ipkg-source",_("Choose upgrade source"), _("\nEdit the upgrade source address." ) + self.oktext))
124
125                 self["menu"] = List(self.list)
126                                 
127                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
128                 {
129                         "ok": self.go,
130                         "back": self.close,
131                         "red": self.close,
132                 }, -1)
133
134                 self.onLayoutFinish.append(self.layoutFinished)
135                 self.backuppath = getBackupPath()
136                 self.backupfile = getBackupFilename()
137                 self.fullbackupfilename = self.backuppath + "/" + self.backupfile
138                 self.onShown.append(self.setWindowTitle)
139                 
140         def layoutFinished(self):
141                 idx = 0
142                 self["menu"].index = idx
143                 
144         def setWindowTitle(self):
145                 self.setTitle(_("Software manager..."))
146                 
147         def go(self):
148                 current = self["menu"].getCurrent()
149                 if current:
150                         current = current[0]
151                         if self.menu == 0:
152                                 if (current == "software-update"):
153                                         self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to update your Dreambox?")+"\n"+_("\nAfter pressing OK, please wait!"))
154                                 elif (current == "software-restore"):
155                                         self.session.open(ImageWizard)
156                                 elif (current == "install-plugins"):
157                                         self.session.open(PluginManager, self.skin_path)
158                                 elif (current == "system-backup"):
159                                         self.session.openWithCallback(self.backupDone,BackupScreen, runBackup = True)
160                                 elif (current == "system-restore"):
161                                         if os_path.exists(self.fullbackupfilename):
162                                                 self.session.openWithCallback(self.startRestore, MessageBox, _("Are you sure you want to restore your Enigma2 backup?\nEnigma2 will restart after the restore"))
163                                         else:   
164                                                 self.session.open(MessageBox, _("Sorry no backups found!"), MessageBox.TYPE_INFO)
165                                 elif (current == "ipkg-install"):
166                                         try:
167                                                 from Plugins.Extensions.MediaScanner.plugin import main
168                                                 main(self.session)
169                                         except:
170                                                 self.session.open(MessageBox, _("Sorry MediaScanner is not installed!"), MessageBox.TYPE_INFO)
171                                 elif (current == "advanced"):
172                                         self.session.open(UpdatePluginMenu, 1)
173                         elif self.menu == 1:
174                                 if (current == "ipkg-manager"):
175                                         self.session.open(PacketManager, self.skin_path)
176                                 elif (current == "backuplocation"):
177                                         parts = [ (r.description, r.mountpoint, self.session) for r in harddiskmanager.getMountedPartitions(onlyhotplug = False)]
178                                         for x in parts:
179                                                 if not access(x[1], F_OK|R_OK|W_OK) or x[1] == '/':
180                                                         parts.remove(x)
181                                         for x in parts:
182                                                 if x[1].startswith('/autofs/'):
183                                                         parts.remove(x)
184                                         if len(parts):
185                                                 self.session.openWithCallback(self.backuplocation_choosen, ChoiceBox, title = _("Please select medium to use as backup location"), list = parts)
186                                 elif (current == "backupfiles"):
187                                         self.session.openWithCallback(self.backupfiles_choosen,BackupSelection)
188                                 elif (current == "advancedrestore"):
189                                         self.session.open(RestoreMenu, self.skin_path)
190                                 elif (current == "ipkg-source"):
191                                         self.session.open(IPKGMenu, self.skin_path)
192
193         def backupfiles_choosen(self, ret):
194                 self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value )
195
196         def backuplocation_choosen(self, option):
197                 if option is not None:
198                         config.plugins.configurationbackup.backuplocation.value = str(option[1])
199                 config.plugins.configurationbackup.backuplocation.save()
200                 config.plugins.configurationbackup.save()
201                 config.save()
202                 self.createBackupfolders()
203         
204         def runUpgrade(self, result):
205                 if result:
206                         self.session.open(UpdatePlugin, self.skin_path)
207
208         def createBackupfolders(self):
209                 print "Creating backup folder if not already there..."
210                 self.backuppath = getBackupPath()
211                 try:
212                         if (os_path.exists(self.backuppath) == False):
213                                 makedirs(self.backuppath)
214                 except OSError:
215                         self.session.open(MessageBox, _("Sorry, your backup destination is not writeable.\n\nPlease choose another one."), MessageBox.TYPE_INFO)
216
217         def backupDone(self,retval = None):
218                 if retval is True:
219                         self.session.open(MessageBox, _("Backup done."), MessageBox.TYPE_INFO)
220                 else:
221                         self.session.open(MessageBox, _("Backup failed."), MessageBox.TYPE_INFO)
222
223         def startRestore(self, ret = False):
224                 if (ret == True):
225                         self.exe = True
226                         self.session.open(RestoreScreen, runRestore = True)
227
228 class IPKGMenu(Screen):
229         skin = """
230                 <screen name="IPKGMenu" position="135,144" size="450,320" title="Select IPKG source......" >
231                         <widget name="filelist" position="10,10" size="430,240" scrollbarMode="showOnDemand" />
232                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,280" zPosition="2" size="140,40" transparent="1" alphatest="on" />
233                         <widget name="closetext" position="20,290" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
234                         <ePixmap pixmap="skin_default/buttons/green.png" position="160,280" zPosition="2" size="140,40" transparent="1" alphatest="on" />
235                         <widget name="edittext" position="170,290" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
236                 </screen>"""
237
238         def __init__(self, session, plugin_path):
239                 Screen.__init__(self, session)
240                 self.skin_path = plugin_path
241                 
242                 self["closetext"] = Label(_("Close"))
243                 self["edittext"] = Label(_("Edit"))
244
245                 self.sel = []
246                 self.val = []
247                 self.entry = False
248                 self.exe = False
249                 
250                 self.path = ""
251
252                 self["actions"] = NumberActionMap(["SetupActions"],
253                 {
254                         "ok": self.KeyOk,
255                         "cancel": self.keyCancel
256                 }, -1)
257
258                 self["shortcuts"] = ActionMap(["ShortcutActions"],
259                 {
260                         "red": self.keyCancel,
261                         "green": self.KeyOk,
262                 })
263                 self.flist = []
264                 self["filelist"] = MenuList(self.flist)
265                 self.fill_list()
266                 self.onLayoutFinish.append(self.layoutFinished)
267
268         def layoutFinished(self):
269                 self.setWindowTitle()
270
271         def setWindowTitle(self):
272                 self.setTitle(_("Select IPKG source to edit..."))
273
274
275         def fill_list(self):
276                 self.flist = []
277                 self.path = '/etc/ipkg/'
278                 if (os_path.exists(self.path) == False):
279                         self.entry = False
280                         return
281                 for file in listdir(self.path):
282                         if (file.endswith(".conf")):
283                                 if file != 'arch.conf':
284                                         self.flist.append((file))
285                                         self.entry = True
286                                         self["filelist"].l.setList(self.flist)
287
288         def KeyOk(self):
289                 if (self.exe == False) and (self.entry == True):
290                         self.sel = self["filelist"].getCurrent()
291                         self.val = self.path + self.sel
292                         self.session.open(IPKGSource, self.val)
293
294         def keyCancel(self):
295                 self.close()
296
297         def Exit(self):
298                 self.close()
299
300
301 class IPKGSource(Screen):
302         skin = """
303                 <screen name="IPKGSource" position="100,100" size="550,80" title="IPKG source" >
304                         <widget name="text" position="10,10" size="530,25" font="Regular;20" backgroundColor="background" foregroundColor="#cccccc" />
305                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,40" zPosition="2" size="140,40" transparent="1" alphatest="on" />
306                         <widget name="closetext" position="20,50" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
307                         <ePixmap pixmap="skin_default/buttons/green.png" position="160,40" zPosition="2" size="140,40" transparent="1" alphatest="on" />
308                         <widget name="edittext" position="170,50" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
309                 </screen>"""
310                 
311         def __init__(self, session, configfile = None):
312                 Screen.__init__(self, session)
313                 self.session = session
314                 self.configfile = configfile
315                 text = ""
316                 if self.configfile:
317                         try:
318                                 fp = file(configfile, 'r')
319                                 sources = fp.readlines()
320                                 if sources:
321                                         text = sources[0]
322                                 fp.close()
323                         except IOError:
324                                 pass
325
326                 desk = getDesktop(0)
327                 x= int(desk.size().width())
328                 y= int(desk.size().height())
329
330                 self["closetext"] = Label(_("Cancel"))
331                 self["edittext"] = Label(_("Save"))
332                 
333                 if (y>=720):
334                         self["text"] = Input(text, maxSize=False, type=Input.TEXT)
335                 else:
336                         self["text"] = Input(text, maxSize=False, visible_width = 55, type=Input.TEXT)
337                         
338                 self["actions"] = NumberActionMap(["WizardActions", "InputActions", "TextEntryActions", "KeyboardInputActions","ShortcutActions"], 
339                 {
340                         "ok": self.go,
341                         "back": self.close,
342                         "red": self.close,
343                         "green": self.go,
344                         "left": self.keyLeft,
345                         "right": self.keyRight,
346                         "home": self.keyHome,
347                         "end": self.keyEnd,
348                         "deleteForward": self.keyDeleteForward,
349                         "deleteBackward": self.keyDeleteBackward,
350                         "1": self.keyNumberGlobal,
351                         "2": self.keyNumberGlobal,
352                         "3": self.keyNumberGlobal,
353                         "4": self.keyNumberGlobal,
354                         "5": self.keyNumberGlobal,
355                         "6": self.keyNumberGlobal,
356                         "7": self.keyNumberGlobal,
357                         "8": self.keyNumberGlobal,
358                         "9": self.keyNumberGlobal,
359                         "0": self.keyNumberGlobal
360                 }, -1)
361
362                 self.onLayoutFinish.append(self.layoutFinished)
363
364         def layoutFinished(self):
365                 self.setWindowTitle()
366                 self["text"].right()
367
368         def setWindowTitle(self):
369                 self.setTitle(_("Edit IPKG source URL..."))
370                 
371         def go(self):
372                 text = self["text"].getText()
373                 if text:
374                         fp = file(self.configfile, 'w')
375                         fp.write(text)
376                         fp.write("\n")
377                         fp.close()
378                 self.close()
379                 
380         def keyLeft(self):
381                 self["text"].left()
382         
383         def keyRight(self):
384                 self["text"].right()
385         
386         def keyHome(self):
387                 self["text"].home()
388         
389         def keyEnd(self):
390                 self["text"].end()
391         
392         def keyDeleteForward(self):
393                 self["text"].delete()
394         
395         def keyDeleteBackward(self):
396                 self["text"].deleteBackward()
397         
398         def keyNumberGlobal(self, number):
399                 self["text"].number(number)
400
401
402 class PacketManager(Screen):
403         skin = """
404                 <screen position="90,80" size="530,420" title="IPKG upgrade..." >
405                         <widget source="list" render="Listbox" position="5,10" size="520,365" scrollbarMode="showOnDemand">
406                                 <convert type="TemplatedMultiContent">
407                                         {"template": [
408                                                         MultiContentEntryText(pos = (5, 1), size = (440, 28), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name
409                                                         MultiContentEntryText(pos = (5, 26), size = (440, 20), font=1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the description
410                                                         MultiContentEntryPixmapAlphaTest(pos = (445, 2), size = (48, 48), png = 4), # index 4 is the status pixmap
411                                                         MultiContentEntryPixmapAlphaTest(pos = (5, 50), size = (510, 2), png = 5), # index 4 is the div pixmap
412                                                 ],
413                                         "fonts": [gFont("Regular", 22),gFont("Regular", 14)],
414                                         "itemHeight": 52
415                                         }
416                                 </convert>
417                         </widget>
418                         <ePixmap pixmap="skin_default/buttons/red.png" position="10,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
419                         <widget name="closetext" position="20,390" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
420                         <ePixmap pixmap="skin_default/buttons/green.png" position="160,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
421                         <widget name="reloadtext" position="170,390" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
422                 </screen>"""
423                 
424         def __init__(self, session, plugin_path, args = None):
425                 Screen.__init__(self, session)
426                 self.session = session
427                 self.skin_path = plugin_path
428
429                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"], 
430                 {
431                         "ok": self.go,
432                         "back": self.exit,
433                         "red": self.exit,
434                         "green": self.reload,
435                 }, -1)
436                 
437                 self.list = []
438                 self.statuslist = []
439                 self["list"] = List(self.list)
440                 self["closetext"] = Label(_("Close"))
441                 self["reloadtext"] = Label(_("Reload"))
442
443                 self.list_updating = True
444                 self.packetlist = []
445                 self.installed_packetlist = {}
446                 self.Console = Console()
447                 self.cmdList = []
448                 self.cachelist = []
449                 self.cache_ttl = 86400  #600 is default, 0 disables, Seconds cache is considered valid (24h should be ok for caching ipkgs)
450                 self.cache_file = '/usr/lib/enigma2/python/Plugins/SystemPlugins/SoftwareManager/packetmanager.cache' #Path to cache directory   
451                 self.oktext = _("\nAfter pressing OK, please wait!")
452                 self.unwanted_extensions = ('-dbg', '-dev', '-doc', 'busybox')
453
454                 self.ipkg = IpkgComponent()
455                 self.ipkg.addCallback(self.ipkgCallback)
456                 self.onShown.append(self.setWindowTitle)
457                 self.onLayoutFinish.append(self.rebuildList)
458
459         def exit(self):
460                 self.ipkg.stop()
461                 if self.Console is not None:
462                         if len(self.Console.appContainers):
463                                 for name in self.Console.appContainers.keys():
464                                         self.Console.kill(name)
465                 self.close()
466
467         def reload(self):
468                 if (os_path.exists(self.cache_file) == True):
469                         remove(self.cache_file)
470                         self.list_updating = True
471                         self.rebuildList()
472                         
473         def setWindowTitle(self):
474                 self.setTitle(_("Packet manager"))
475
476         def setStatus(self,status = None):
477                 if status:
478                         self.statuslist = []
479                         divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
480                         if status == 'update':
481                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/upgrade.png"))
482                                 self.statuslist.append(( _("Package list update"), '', _("Trying to download a new packetlist. Please wait..." ),'',statuspng, divpng ))
483                                 self['list'].setList(self.statuslist)   
484                         elif status == 'error':
485                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/remove.png"))
486                                 self.statuslist.append(( _("Error"), '', _("There was an error downloading the packetlist. Please try again." ),'',statuspng, divpng ))
487                                 self['list'].setList(self.statuslist)                           
488
489         def rebuildList(self):
490                 self.setStatus('update')
491                 self.inv_cache = 0
492                 self.vc = valid_cache(self.cache_file, self.cache_ttl)
493                 if self.cache_ttl > 0 and self.vc != 0:
494                         try:
495                                 self.buildPacketList()
496                         except:
497                                 self.inv_cache = 1
498                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
499                         self.run = 0
500                         self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
501
502         def go(self, returnValue = None):
503                 cur = self["list"].getCurrent()
504                 if cur:
505                         status = cur[3]
506                         package = cur[0]
507                         self.cmdList = []
508                         if status == 'installed':
509                                 self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": package }))
510                                 if len(self.cmdList):
511                                         self.session.openWithCallback(self.runRemove, MessageBox, _("Do you want to remove the package:\n") + package + "\n" + self.oktext)
512                         elif status == 'upgradeable':
513                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package }))
514                                 if len(self.cmdList):
515                                         self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to upgrade the package:\n") + package + "\n" + self.oktext)
516                         elif status == "installable":
517                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package }))
518                                 if len(self.cmdList):
519                                         self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n") + package + "\n" + self.oktext)
520
521         def runRemove(self, result):
522                 if result:
523                         self.session.openWithCallback(self.runRemoveFinished, Ipkg, cmdList = self.cmdList)
524
525         def runRemoveFinished(self):
526                 self.session.openWithCallback(self.RemoveReboot, MessageBox, _("Remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
527
528         def RemoveReboot(self, result):
529                 if result is None:
530                         return
531                 if result is False:
532                         cur = self["list"].getCurrent()
533                         if cur:
534                                 item = self['list'].getIndex()
535                                 self.list[item] = self.buildEntryComponent(cur[0], cur[1], cur[2], 'installable')
536                                 self.cachelist[item] = [cur[0], cur[1], cur[2], 'installable']
537                                 self['list'].setList(self.list)
538                                 write_cache(self.cache_file, self.cachelist)
539                                 self.reloadPluginlist()
540                 if result:
541                         quitMainloop(3)
542
543         def runUpgrade(self, result):
544                 if result:
545                         self.session.openWithCallback(self.runUpgradeFinished, Ipkg, cmdList = self.cmdList)
546
547         def runUpgradeFinished(self):
548                 self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
549                 
550         def UpgradeReboot(self, result):
551                 if result is None:
552                         return
553                 if result is False:
554                         cur = self["list"].getCurrent()
555                         if cur:
556                                 item = self['list'].getIndex()
557                                 self.list[item] = self.buildEntryComponent(cur[0], cur[1], cur[2], 'installed')
558                                 self.cachelist[item] = [cur[0], cur[1], cur[2], 'installed']
559                                 self['list'].setList(self.list)
560                                 write_cache(self.cache_file, self.cachelist)
561                                 self.reloadPluginlist()
562                 if result:
563                         quitMainloop(3)
564
565         def ipkgCallback(self, event, param):
566                 if event == IpkgComponent.EVENT_ERROR:
567                         self.list_updating = False
568                         self.setStatus('error')
569                 elif event == IpkgComponent.EVENT_DONE:
570                         if self.list_updating:
571                                 self.list_updating = False
572                                 if not self.Console:
573                                         self.Console = Console()
574                                 cmd = "ipkg list"
575                                 self.Console.ePopen(cmd, self.IpkgList_Finished)
576                 #print event, "-", param
577                 pass
578
579         def IpkgList_Finished(self, result, retval, extra_args = None):
580                 if len(result):
581                         self.packetlist = []
582                         for x in result.splitlines():
583                                 split = x.split(' - ')   #self.blacklisted_packages
584                                 if not any(split[0].strip().endswith(x) for x in self.unwanted_extensions):
585                                         self.packetlist.append([split[0].strip(), split[1].strip(),split[2].strip()])
586                 if not self.Console:
587                         self.Console = Console()
588                 cmd = "ipkg list_installed"
589                 self.Console.ePopen(cmd, self.IpkgListInstalled_Finished)
590
591         def IpkgListInstalled_Finished(self, result, retval, extra_args = None):
592                 if len(result):
593                         self.installed_packetlist = {}
594                         for x in result.splitlines():
595                                 split = x.split(' - ')
596                                 if not any(split[0].strip().endswith(x) for x in self.unwanted_extensions):
597                                         self.installed_packetlist[split[0].strip()] = split[1].strip()
598                 self.buildPacketList()
599
600         def buildEntryComponent(self, name, version, description, state):
601                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
602                 if state == 'installed':
603                         installedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installed.png"))
604                         return((name, version, description, state, installedpng, divpng))       
605                 elif state == 'upgradeable':
606                         upgradeablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/upgradeable.png"))
607                         return((name, version, description, state, upgradeablepng, divpng))     
608                 else:
609                         installablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installable.png"))
610                         return((name, version, description, state, installablepng, divpng))
611
612         def buildPacketList(self):
613                 self.list = []
614                 self.cachelist = []
615
616                 if self.cache_ttl > 0 and self.vc != 0:
617                         print 'Loading packagelist cache from ',self.cache_file
618                         try:
619                                 self.cachelist = load_cache(self.cache_file)
620                                 if len(self.cachelist) > 0:
621                                         for x in self.cachelist:
622                                                 self.list.append(self.buildEntryComponent(x[0], x[1], x[2], x[3]))
623                                         self['list'].setList(self.list)
624                         except:
625                                 self.inv_cache = 1
626
627                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
628                         print 'rebuilding fresh package list'
629                         for x in self.packetlist:
630                                 status = ""
631                                 if self.installed_packetlist.has_key(x[0].strip()):
632                                         if self.installed_packetlist[x[0].strip()] == x[1].strip():
633                                                 status = "installed"
634                                                 self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status))
635                                         else:
636                                                 status = "upgradeable"
637                                                 self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status))
638                                 else:
639                                         status = "installable"
640                                         self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status))
641                                 if not any(x[0].strip().endswith(x) for x in self.unwanted_extensions):
642                                         self.cachelist.append([x[0].strip(), x[1].strip(), x[2].strip(), status])       
643                         write_cache(self.cache_file, self.cachelist)
644                         self['list'].setList(self.list)
645
646         def reloadPluginlist(self):
647                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
648
649
650 class PluginManager(Screen, DreamInfoHandler):
651
652         lastDownloadDate = None
653
654         skin = """
655                 <screen position="80,80" size="560,440" title="Plugin manager..." >
656                         <widget source="list" render="Listbox" position="5,5" size="550,360" scrollbarMode="showOnDemand">
657                                 <convert type="TemplatedMultiContent">
658                                 {"templates":
659                                         {"default": (51,[
660                                                         MultiContentEntryText(pos = (30, 1), size = (470, 24), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name
661                                                         MultiContentEntryText(pos = (30, 25), size = (470, 20), font=1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the description
662                                                         MultiContentEntryPixmapAlphaTest(pos = (475, 0), size = (48, 48), png = 5), # index 5 is the status pixmap
663                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 49), size = (550, 2), png = 6), # index 6 is the div pixmap
664                                                 ]),
665                                         "category": (40,[
666                                                         MultiContentEntryText(pos = (30, 0), size = (500, 23), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name
667                                                         MultiContentEntryText(pos = (30, 24), size = (500, 14), font=1, flags = RT_HALIGN_LEFT, text = 1), # index 1 is the description
668                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 38), size = (550, 2), png = 3), # index 3 is the div pixmap
669                                                 ])
670                                         },
671                                         "fonts": [gFont("Regular", 22),gFont("Regular", 14)],
672                                         "itemHeight": 52
673                                 }
674                                 </convert>
675                         </widget>
676                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,370" zPosition="2" size="140,40" transparent="1" alphatest="on" />
677                         <widget name="closetext" position="0,370" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
678                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,370" zPosition="2" size="140,40" transparent="1" alphatest="on" />
679                         <widget name="installtext" position="140,370" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
680                         <ePixmap pixmap="skin_default/buttons/yellow.png" position="280,370" zPosition="2" size="140,40" transparent="1" alphatest="on" />
681                         <widget name="viewtext" position="280,370" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
682                         <ePixmap pixmap="skin_default/buttons/blue.png" position="420,370" zPosition="2" size="140,40" transparent="1" alphatest="on" />
683                         <widget name="bluetext" position="420,370" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
684                         <widget name="status" position="10,410" zPosition="10" size="540,30" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
685                 </screen>"""
686
687         def __init__(self, session, plugin_path, args = None):
688                 Screen.__init__(self, session)
689                 self.session = session
690                 self.skin_path = plugin_path
691                 aboutInfo = about.getImageVersionString()
692                 if aboutInfo.startswith("dev-"):
693                         self.ImageVersion = 'Experimental'
694                 else:
695                         self.ImageVersion = 'Stable'
696                 self.language = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country"
697
698                 DreamInfoHandler.__init__(self, self.statusCallback, blocking = False, neededTag = 'ALL_TAGS', neededFlag = self.ImageVersion, language = self.language)
699                 self.directory = resolveFilename(SCOPE_METADIR)
700
701                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions", "InfobarEPGActions", "HelpActions" ],
702                 {
703                         "ok": self.handleCurrent,
704                         "back": self.exit,
705                         "red": self.exit,
706                         "green": self.handleCurrent,
707                         "yellow": self.handleSelected,
708                         "showEventInfo": self.handleSelected,
709                         "displayHelp": self.handleHelp,
710                 }, -1)
711
712                 self.list = []
713                 self.statuslist = []
714                 self.selectedFiles = []
715                 self.categoryList = []
716                 self["list"] = List(self.list)
717                 self["closetext"] = Label(_("Close"))
718                 self["installtext"] = Label()
719                 self["viewtext"] = Label()
720                 self["bluetext"] = Label()
721                 self["status"] = Label()
722
723                 self.list_updating = True
724                 self.packetlist = []
725                 self.installed_packetlist = {}
726                 self.available_packetlist = []
727                 self.available_updates = 0
728                 self.Console = Console()
729                 self.cmdList = []
730                 self.oktext = _("\nAfter pressing OK, please wait!")
731                 self.unwanted_extensions = ('-dbg', '-dev', '-doc')
732
733                 self.ipkg = IpkgComponent()
734                 self.ipkg.addCallback(self.ipkgCallback)
735                 if not self.selectionChanged in self["list"].onSelectionChanged:
736                         self["list"].onSelectionChanged.append(self.selectionChanged)
737
738                 self["installtext"].hide()
739                 self["bluetext"].hide()
740                 self["viewtext"].hide()
741                 self["status"].hide()
742                 self.currList = ""
743                 self.currentSelectedTag = None
744                 self.currentSelectedIndex = None
745
746                 self.onShown.append(self.setWindowTitle)
747                 self.onLayoutFinish.append(self.rebuildList)
748
749         def setWindowTitle(self):
750                 self.setTitle(_("Plugin manager"))
751
752         def exit(self):
753                 if self.currList == "packages":
754                         self.currList = "category"
755                         self.currentSelectedTag = None
756                         self["list"].style = "category"
757                         self['list'].setList(self.categoryList)
758                         self["list"].setIndex(self.currentSelectedIndex)
759                         self["list"].updateList(self.categoryList)
760                         self.selectionChanged()
761                 else:
762                         self.ipkg.stop()
763                         if self.Console is not None:
764                                 if len(self.Console.appContainers):
765                                         for name in self.Console.appContainers.keys():
766                                                 self.Console.kill(name)
767                         if len(self.cmdList) and len(self.selectedFiles):
768                                 self.session.openWithCallback(self.runInstall, PluginManagerInfo, self.skin_path, self.cmdList)
769                         elif not len(self.cmdList) and self.available_updates > 0:
770                                 self.cmdList = []
771                                 self.cmdList.append((IpkgComponent.CMD_UPGRADE, { "test_only": False }))
772                                 self.session.openWithCallback(self.runInstall, PluginManagerInfo, self.skin_path, self.cmdList)
773                         else:
774                                 self.close()
775
776         def handleHelp(self):
777                 if self.currList != "status":
778                         self.session.open(PluginManagerHelp, self.skin_path)
779
780         def runInstall(self, result):
781                 if result:
782                         self.session.openWithCallback(self.runExecuteFinished, Ipkg, cmdList = self.cmdList)
783                 else:
784                         self.close()
785
786         def setState(self,status = None):
787                 if status:
788                         self.currList = "status"
789                         self.statuslist = []
790                         self["installtext"].hide()
791                         self["bluetext"].hide()
792                         self["viewtext"].hide()
793                         divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
794                         if status == 'update':
795                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/upgrade.png"))
796                                 self.statuslist.append(( _("Package list update"), '', _("Trying to download a new packetlist. Please wait..." ),'', '', statuspng, divpng, None, '' ))
797                                 self["list"].style = "default"
798                                 self['list'].setList(self.statuslist)
799                         elif status == 'sync':
800                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/upgrade.png"))
801                                 self.statuslist.append(( _("Package list update"), '', _("Searching for new installed or removed packages. Please wait..." ),'', '', statuspng, divpng, None, '' ))
802                                 self["list"].style = "default"
803                                 self['list'].setList(self.statuslist)
804                         elif status == 'error':
805                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/remove.png"))
806                                 self.statuslist.append(( _("Error"), '', _("There was an error downloading the packetlist. Please try again." ),'', '', statuspng, divpng, None, '' ))
807                                 self["list"].style = "default"
808                                 self['list'].setList(self.statuslist)
809
810         def statusCallback(self, status, progress):
811                 pass
812
813         def selectionChanged(self):
814                 current = self["list"].getCurrent()
815                 self["status"].hide()
816                 if current:
817                         if self.currList == "packages":
818                                 self["closetext"].setText(_("Back"))
819                                 if current[4] == 'installed':
820                                         self["installtext"].setText(_("Remove"))
821                                 elif current[4] == 'installable':
822                                         self["installtext"].setText(_("Install"))
823                                 elif current[4] == 'remove':
824                                         self["installtext"].setText(_("Undo\nRemove"))
825                                 elif current[4] == 'install':
826                                         self["installtext"].setText(_("Undo\nInstall"))
827                                 self["installtext"].show()
828                                 self["viewtext"].setText(_("View details"))
829                                 self["viewtext"].show()
830                                 self["bluetext"].hide()
831                                 if len(self.selectedFiles) == 0 and self.available_updates is not 0:
832                                         self["status"].setText(_("There are at least ") + str(self.available_updates) + _(" updates available."))
833                                         self["status"].show()
834                                 elif len(self.selectedFiles) is not 0:
835                                         self["status"].setText(str(len(self.selectedFiles)) + _(" packages selected."))
836                                         self["status"].show()
837                         elif self.currList == "category":
838                                 self["closetext"].setText(_("Close"))
839                                 self["installtext"].hide()
840                                 self["bluetext"].hide()
841                                 if len(self.selectedFiles) == 0 and self.available_updates is not 0:
842                                         self["status"].setText(_("There are at least ") + str(self.available_updates) + _(" updates available."))
843                                         self["status"].show()
844                                         self["viewtext"].setText(_("Update"))
845                                         self["viewtext"].show()
846                                 elif len(self.selectedFiles) is not 0:
847                                         self["status"].setText(str(len(self.selectedFiles)) + _(" packages selected."))
848                                         self["status"].show()
849                                         self["viewtext"].setText(_("Process"))
850                                         self["viewtext"].show()
851                         self["closetext"].show()
852
853         def getSelectionState(self, detailsFile):
854                 for entry in self.selectedFiles:
855                         if entry[0] == detailsFile:
856                                 return True
857                 return False
858
859         def rebuildList(self):
860                 self.setState('update')
861                 if not PluginManager.lastDownloadDate or (time() - PluginManager.lastDownloadDate) > 3600:
862                         # Only update from internet once per hour
863                         PluginManager.lastDownloadDate = time()
864                         print "last update time > 1h"
865                         self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
866                 else:
867                         print "last update time < 1h"
868                         self.startIpkgList()
869
870         def ipkgCallback(self, event, param):
871                 if event == IpkgComponent.EVENT_ERROR:
872                         self.list_updating = False
873                         self.setState('error')
874                 elif event == IpkgComponent.EVENT_DONE:
875                         self.startIpkgList()
876                 pass
877
878         def startIpkgList(self):
879                 if self.list_updating:
880                         if not self.Console:
881                                 self.Console = Console()
882                         cmd = "ipkg list"
883                         self.Console.ePopen(cmd, self.IpkgList_Finished)
884
885         def IpkgList_Finished(self, result, retval, extra_args = None):
886                 if len(result):
887                         self.available_packetlist = []
888                         for x in result.splitlines():
889                                 split = x.split(' - ')
890                                 if not any(split[0].strip().endswith(x) for x in self.unwanted_extensions):
891                                         self.available_packetlist.append([split[0].strip(), split[1].strip(), split[2].strip()])
892                 self.startInstallMetaPackage()
893
894         def startInstallMetaPackage(self):
895                 if self.list_updating:
896                         self.list_updating = False
897                         if not self.Console:
898                                 self.Console = Console()
899                         cmd = "ipkg install enigma2-meta" #dummy,will change probably"
900                         self.Console.ePopen(cmd, self.InstallMetaPackage_Finished)
901
902         def InstallMetaPackage_Finished(self, result, retval, extra_args = None):
903                 if len(result):
904                         self.fillPackagesIndexList()
905                 if not self.Console:
906                         self.Console = Console()
907                 self.setState('sync')
908                 cmd = "ipkg list_installed"
909                 self.Console.ePopen(cmd, self.IpkgListInstalled_Finished)
910
911         def IpkgListInstalled_Finished(self, result, retval, extra_args = None):
912                 if len(result):
913                         self.installed_packetlist = {}
914                         for x in result.splitlines():
915                                 split = x.split(' - ')
916                                 if not any(split[0].strip().endswith(x) for x in self.unwanted_extensions):
917                                         self.installed_packetlist[split[0].strip()] = split[1].strip()
918                 self.countUpdates()
919                 if self.currentSelectedTag is None:
920                         self.buildCategoryList()
921                 else:
922                         self.buildPacketList(self.currentSelectedTag)
923
924         def countUpdates(self):
925                 self.available_updates = 0
926                 for package in self.packagesIndexlist[:]:
927                         attributes = package[0]["attributes"]
928                         packagename = attributes["packagename"]
929                         for x in self.available_packetlist:
930                                 if x[0].strip() == packagename:
931                                         if self.installed_packetlist.has_key(packagename):
932                                                 if self.installed_packetlist[packagename] != x[1].strip():
933                                                         self.available_updates +=1
934
935         def handleCurrent(self):
936                 current = self["list"].getCurrent()
937                 if current:
938                         if self.currList == "category":
939                                 self.currentSelectedIndex = self["list"].index
940                                 selectedTag = current[2]
941                                 self.buildPacketList(selectedTag)
942                         elif self.currList == "packages":
943                                 if current[7] is not '':
944                                         idx = self["list"].getIndex()
945                                         detailsFile = self.list[idx][1]
946                                         if self.list[idx][7] == True:
947                                                 for entry in self.selectedFiles:
948                                                         if entry[0] == detailsFile:
949                                                                 self.selectedFiles.remove(entry)
950                                         else:
951                                                 alreadyinList = False
952                                                 for entry in self.selectedFiles:
953                                                         if entry[0] == detailsFile:
954                                                                 alreadyinList = True
955                                                 if not alreadyinList:
956                                                         self.selectedFiles.append((detailsFile,current[4],current[3]))
957                                         if current[4] == 'installed':
958                                                 self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'remove', True)
959                                         elif current[4] == 'installable':
960                                                 self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'install', True)
961                                         elif current[4] == 'remove':
962                                                 self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'installed', False)
963                                         elif current[4] == 'install':
964                                                 self.list[idx] = self.buildEntryComponent(current[0], current[1], current[2], current[3], 'installable',False)
965                                         self["list"].setList(self.list)
966                                         self["list"].setIndex(idx)
967                                         self["list"].updateList(self.list)
968                                         self.selectionChanged()
969
970         def handleSelected(self):
971                 current = self["list"].getCurrent()
972                 if current:
973                         if self.currList == "packages":
974                                 if current[7] is not '':
975                                         detailsfile = self.directory[0] + "/" + current[1]
976                                         if (os_path.exists(detailsfile) == True):
977                                                 self.session.openWithCallback(self.detailsClosed, PluginDetails, self.skin_path, current)
978                                         else:
979                                                 self.session.open(MessageBox, _("Sorry, no Details available!"), MessageBox.TYPE_INFO)
980                         elif self.currList == "category":
981                                 self.installPlugins()
982
983         def detailsClosed(self, result):
984                 if result:
985                         if not self.Console:
986                                 self.Console = Console()
987                         self.setState('sync')
988                         PluginManager.lastDownloadDate = time()
989                         self.selectedFiles = []
990                         cmd = "ipkg update"
991                         self.Console.ePopen(cmd, self.InstallMetaPackage_Finished)
992
993         def buildEntryComponent(self, name, details, description, packagename, state, selected = False):
994                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
995                 if state == 'installed':
996                         installedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installed.png"))
997                         return((name, details, description, packagename, state, installedpng, divpng, selected))
998                 elif state == 'installable':
999                         installablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installable.png"))
1000                         return((name, details, description, packagename, state, installablepng, divpng, selected))
1001                 elif state == 'remove':
1002                         removepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/remove.png"))
1003                         return((name, details, description, packagename, state, removepng, divpng, selected))
1004                 elif state == 'install':
1005                         installpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/install.png"))
1006                         return((name, details, description, packagename, state, installpng, divpng, selected))
1007
1008         def buildPacketList(self, categorytag = None):
1009                 if categorytag is not None:
1010                         self.currList = "packages"
1011                         self.currentSelectedTag = categorytag
1012                         self.packetlist = []
1013                         for package in self.packagesIndexlist[:]:
1014                                 prerequisites = package[0]["prerequisites"]
1015                                 if prerequisites.has_key("tag"):
1016                                         for foundtag in prerequisites["tag"]:
1017                                                 if categorytag == foundtag:
1018                                                         attributes = package[0]["attributes"]
1019                                                         if attributes.has_key("packagetype"):
1020                                                                 if attributes["packagetype"] == "internal":
1021                                                                         continue
1022                                                                 self.packetlist.append([attributes["name"], attributes["details"], attributes["shortdescription"], attributes["packagename"]])
1023                                                         else:
1024                                                                 self.packetlist.append([attributes["name"], attributes["details"], attributes["shortdescription"], attributes["packagename"]])
1025                         self.list = []
1026                         for x in self.packetlist:
1027                                 status = ""
1028                                 selectState = self.getSelectionState(x[1].strip())
1029                                 if self.installed_packetlist.has_key(x[3].strip()):
1030                                         if selectState == True:
1031                                                 status = "remove"
1032                                         else:
1033                                                 status = "installed"
1034                                         self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), x[3].strip(), status, selected = selectState))
1035                                 else:
1036                                         if selectState == True:
1037                                                 status = "install"
1038                                         else:
1039                                                 status = "installable"
1040                                         self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), x[3].strip(), status, selected = selectState))
1041                         if len(self.list):
1042                                 self.list.sort(key=lambda x: x[0])
1043                         self["list"].style = "default"
1044                         self['list'].setList(self.list)
1045                         self["list"].updateList(self.list)
1046                         self.selectionChanged()
1047
1048         def buildCategoryList(self):
1049                 self.currList = "category"
1050                 self.categories = []
1051                 self.categoryList = []
1052                 for package in self.packagesIndexlist[:]:
1053                         prerequisites = package[0]["prerequisites"]
1054                         if prerequisites.has_key("tag"):
1055                                 for foundtag in prerequisites["tag"]:
1056                                         attributes = package[0]["attributes"]
1057                                         if foundtag not in self.categories:
1058                                                 self.categories.append(foundtag)
1059                                                 self.categoryList.append(self.buildCategoryComponent(foundtag))
1060                 self.categoryList.sort(key=lambda x: x[0])
1061                 self["list"].style = "category"
1062                 self['list'].setList(self.categoryList)
1063                 self["list"].updateList(self.categoryList)
1064                 self.selectionChanged()
1065
1066         def buildCategoryComponent(self, tag = None):
1067                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
1068                 if tag is not None:
1069                         if tag == 'System':
1070                                 return(( _("System"), _("View list of available system extensions" ), tag, divpng ))
1071                         elif tag == 'Skin':
1072                                 return(( _("Skins"), _("View list of available skins" ), tag, divpng ))
1073                         elif tag == 'Recording':
1074                                 return(( _("Recordings"), _("View list of available recording extensions" ), tag, divpng ))
1075                         elif tag == 'Network':
1076                                 return(( _("Network"), _("View list of available networking extensions" ), tag, divpng ))
1077                         elif tag == 'CI':
1078                                 return(( _("CommonInterface"), _("View list of available CommonInterface extensions" ), tag, divpng ))
1079                         elif tag == 'Default':
1080                                 return(( _("Default Settings"), _("View list of available default settings" ), tag, divpng ))
1081                         elif tag == 'SAT':
1082                                 return(( _("Satteliteequipment"), _("View list of available Satteliteequipment extensions." ), tag, divpng ))
1083                         elif tag == 'Software':
1084                                 return(( _("Software"), _("View list of available software extensions" ), tag, divpng ))
1085                         elif tag == 'Multimedia':
1086                                 return(( _("Multimedia"), _("View list of available multimedia extensions." ), tag, divpng ))
1087                         elif tag == 'Display':
1088                                 return(( _("Display and Userinterface"), _("View list of available Display and Userinterface extensions." ), tag, divpng ))
1089                         elif tag == 'EPG':
1090                                 return(( _("Electronic Program Guide"), _("View list of available EPG extensions." ), tag, divpng ))
1091                         elif tag == 'Communication':
1092                                 return(( _("Communication"), _("View list of available communication extensions." ), tag, divpng ))
1093                         else: # dynamically generate non existent tags
1094                                 return(( str(tag), _("View list of available ") + str(tag) + _(" extensions." ), tag, divpng ))
1095
1096         def installPlugins(self):
1097                 self.cmdList = []
1098                 if self.available_updates > 0:
1099                         self.cmdList.append((IpkgComponent.CMD_UPGRADE, { "test_only": False }))
1100                 if self.selectedFiles and len(self.selectedFiles):
1101                         for plugin in self.selectedFiles:
1102                                 detailsfile = self.directory[0] + "/" + plugin[0]
1103                                 if (os_path.exists(detailsfile) == True):
1104                                         self.fillPackageDetails(plugin[0])
1105                                         self.package = self.packageDetails[0]
1106                                         if self.package[0].has_key("attributes"):
1107                                                 self.attributes = self.package[0]["attributes"]
1108                                         if self.attributes.has_key("package"):
1109                                                 self.packagefiles = self.attributes["package"]
1110                                         if plugin[1] == 'installed':
1111                                                 if self.packagefiles:
1112                                                         for package in self.packagefiles[:]:
1113                                                                 self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": package["name"] }))
1114                                                 else:
1115                                                         self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": plugin[2] }))
1116                                         else:
1117                                                 if self.packagefiles:
1118                                                         for package in self.packagefiles[:]:
1119                                                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package["name"] }))
1120                                                 else:
1121                                                         self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": plugin[2] }))
1122                                 else:
1123                                         if plugin[1] == 'installed':
1124                                                 self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": plugin[2] }))
1125                                         else:
1126                                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": plugin[2] }))
1127                 if len(self.cmdList):
1128                         self.session.openWithCallback(self.runExecute, PluginManagerInfo, self.skin_path, self.cmdList)
1129
1130         def runExecute(self, result):
1131                 if result:
1132                         self.session.openWithCallback(self.runExecuteFinished, Ipkg, cmdList = self.cmdList)
1133
1134         def runExecuteFinished(self):
1135                 self.session.openWithCallback(self.ExecuteReboot, MessageBox, _("Install or remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1136
1137         def ExecuteReboot(self, result):
1138                 if result is None:
1139                         return
1140                 if result is False:
1141                         self.reloadPluginlist()
1142                         self.detailsClosed(True)
1143                 if result:
1144                         quitMainloop(3)
1145
1146         def reloadPluginlist(self):
1147                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
1148
1149
1150 class PluginManagerInfo(Screen):
1151         skin = """
1152                 <screen position="80,80" size="560,450" title="Plugin manager job information..." >
1153                         <widget name="status" position="5,5" zPosition="10" size="540,30" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
1154                         <ePixmap pixmap="skin_default/div-h.png" position="0,35" zPosition="10" size="550,2" transparent="1" alphatest="on" />
1155                         <widget source="list" render="Listbox" position="5,45" size="550,360" scrollbarMode="showOnDemand" selectionDisabled="1">
1156                                 <convert type="TemplatedMultiContent">
1157                                         {"template": [
1158                                                         MultiContentEntryText(pos = (50, 1), size = (150, 24), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name
1159                                                         MultiContentEntryText(pos = (50, 25), size = (540, 24), font=1, flags = RT_HALIGN_LEFT, text = 1), # index 1 is the state
1160                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 1), size = (48, 48), png = 2), # index 2 is the status pixmap
1161                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 49), size = (550, 2), png = 3), # index 3 is the div pixmap
1162                                                 ],
1163                                         "fonts": [gFont("Regular", 22),gFont("Regular", 18)],
1164                                         "itemHeight": 52
1165                                         }
1166                                 </convert>
1167                         </widget>
1168                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,410" zPosition="2" size="140,40" transparent="1" alphatest="on" />
1169                         <widget name="closetext" position="0,410" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
1170                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,410" zPosition="2" size="140,40" transparent="1" alphatest="on" />
1171                         <widget name="continuetext" position="140,410" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
1172                 </screen>"""
1173
1174         def __init__(self, session, plugin_path, cmdlist = None):
1175                 Screen.__init__(self, session)
1176                 self.session = session
1177                 self.skin_path = plugin_path
1178                 self.cmdlist = cmdlist
1179
1180                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
1181                 {
1182                         "ok": self.process,
1183                         "back": self.exit,
1184                         "red": self.exit,
1185                         "green": self.process,
1186                 }, -1)
1187
1188                 self.list = []
1189                 self["list"] = List(self.list)
1190                 self["closetext"] = Label(_("Cancel"))
1191                 self["continuetext"] = Label(_("Continue"))
1192                 self["status"] = Label(_("Following tasks will be done after you press continue."))
1193
1194                 self.onShown.append(self.setWindowTitle)
1195                 self.onLayoutFinish.append(self.rebuildList)
1196
1197         def setWindowTitle(self):
1198                 self.setTitle(_("Plugin manager process information..."))
1199
1200         def rebuildList(self):
1201                 self.list = []
1202                 if self.cmdlist is not None:
1203                         for entry in self.cmdlist:
1204                                 cmd = entry[0]
1205                                 if cmd == 0:
1206                                         action = 'install'
1207                                 elif cmd == 2:
1208                                         action = 'remove'
1209                                 else:
1210                                         action = 'upgrade'
1211
1212                                 args = entry[1]
1213                                 if cmd == 0:
1214                                         info = args['package']
1215                                 elif cmd == 2:
1216                                         info = args['package']
1217                                 else:
1218                                         info = _("Dreambox software because updates are available.")
1219
1220                                 self.list.append(self.buildEntryComponent(action,info))
1221                         self['list'].setList(self.list)
1222                         self['list'].updateList(self.list)
1223
1224         def buildEntryComponent(self, action,info):
1225                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
1226                 upgradepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/upgrade.png"))
1227                 installpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/install.png"))
1228                 removepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/remove.png"))
1229                 if action == 'install':
1230                         return(( _('Installing'), info, installpng, divpng))
1231                 elif action == 'remove':
1232                         return(( _('Remove'), info, removepng, divpng))
1233                 else:
1234                         return(( _('Upgrade'), info, upgradepng, divpng))
1235
1236         def exit(self):
1237                 self.close(False)
1238
1239         def process(self):
1240                 self.close(True)
1241
1242
1243 class PluginManagerHelp(Screen):
1244         skin = """
1245                 <screen position="80,80" size="560,450" title="Plugin manager help..." >
1246                         <widget name="status" position="5,5" zPosition="10" size="540,30" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
1247                         <ePixmap pixmap="skin_default/div-h.png" position="0,35" zPosition="10" size="550,2" transparent="1" alphatest="on" />
1248                         <widget source="list" render="Listbox" position="5,45" size="550,360" scrollbarMode="showOnDemand" selectionDisabled="1">
1249                                 <convert type="TemplatedMultiContent">
1250                                         {"template": [
1251                                                         MultiContentEntryText(pos = (50, 1), size = (540, 24), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name
1252                                                         MultiContentEntryText(pos = (50, 25), size = (540, 24), font=1, flags = RT_HALIGN_LEFT, text = 1), # index 1 is the state
1253                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 1), size = (48, 48), png = 2), # index 2 is the status pixmap
1254                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 49), size = (550, 2), png = 3), # index 3 is the div pixmap
1255                                                 ],
1256                                         "fonts": [gFont("Regular", 22),gFont("Regular", 18)],
1257                                         "itemHeight": 52
1258                                         }
1259                                 </convert>
1260                         </widget>
1261                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,410" zPosition="2" size="140,40" transparent="1" alphatest="on" />
1262                         <widget name="closetext" position="0,410" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
1263                 </screen>"""
1264
1265         def __init__(self, session, plugin_path):
1266                 Screen.__init__(self, session)
1267                 self.session = session
1268                 self.skin_path = plugin_path
1269
1270                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
1271                 {
1272                         "back": self.exit,
1273                         "red": self.exit,
1274                 }, -1)
1275
1276                 self.list = []
1277                 self["list"] = List(self.list)
1278                 self["closetext"] = Label(_("Close"))
1279                 self["status"] = Label(_("Here is a small overview of the available icon states."))
1280
1281                 self.onShown.append(self.setWindowTitle)
1282                 self.onLayoutFinish.append(self.rebuildList)
1283
1284         def setWindowTitle(self):
1285                 self.setTitle(_("Plugin manager help..."))
1286
1287         def rebuildList(self):
1288                 self.list = []
1289                 self.list.append(self.buildEntryComponent('install'))
1290                 self.list.append(self.buildEntryComponent('installable'))
1291                 self.list.append(self.buildEntryComponent('installed'))
1292                 self.list.append(self.buildEntryComponent('remove'))
1293                 self['list'].setList(self.list)
1294                 self['list'].updateList(self.list)
1295
1296         def buildEntryComponent(self, state):
1297                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
1298                 installedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installed.png"))
1299                 installablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installable.png"))
1300                 removepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/remove.png"))
1301                 installpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/install.png"))
1302
1303                 if state == 'installed':
1304                         return(( _('This plugin is installed.'), _('You can remove this plugin.'), installedpng, divpng))
1305                 elif state == 'installable':
1306                         return(( _('This plugin is not installed.'), _('You can install this plugin.'), installablepng, divpng))
1307                 elif state == 'install':
1308                         return(( _('This plugin will be installed.'), _('You can cancel the installation.'), installpng, divpng))
1309                 elif state == 'remove':
1310                         return(( _('This plugin will be removed.'), _('You can cancel the removal.'), removepng, divpng))
1311
1312         def exit(self):
1313                 self.close()
1314
1315
1316 class PluginDetails(Screen, DreamInfoHandler):
1317         skin = """
1318                 <screen name="PluginDetails" position="60,90" size="600,420" title="PluginDetails..." >
1319                         <widget name="author" position="10,10" size="500,25" zPosition="10" font="Regular;21" transparent="1" />
1320                         <widget name="statuspic" position="550,0" size="48,48" alphatest="on"/>
1321                         <widget name="divpic" position="0,40" size="600,2" alphatest="on"/>
1322                         <widget name="detailtext" position="10,50" size="270,330" zPosition="10" font="Regular;21" transparent="1" halign="left" valign="top"/>
1323                         <widget name="screenshot" position="290,50" size="300,330" alphatest="on"/>
1324                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
1325                         <widget name="closetext" position="0,380" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
1326                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
1327                         <widget name="statetext" position="140,380" zPosition="10" size="140,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
1328                 </screen>"""
1329         def __init__(self, session, plugin_path, packagedata = None):
1330                 Screen.__init__(self, session)
1331                 self.skin_path = plugin_path
1332                 self.language = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country"
1333                 self.attributes = None
1334                 self.translatedAttributes = None
1335                 DreamInfoHandler.__init__(self, self.statusCallback, blocking = False, language = self.language)
1336                 self.directory = resolveFilename(SCOPE_METADIR)
1337                 if packagedata:
1338                         self.pluginname = packagedata[0]
1339                         self.details = packagedata[1]
1340                         self.pluginstate = packagedata[4]
1341                         self.statuspicinstance = packagedata[5]
1342                         self.divpicinstance = packagedata[6]
1343                         self.fillPackageDetails(self.details)
1344
1345                 self.thumbnail = ""
1346
1347                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
1348                 {
1349                         "back": self.exit,
1350                         "red": self.exit,
1351                         "green": self.go,
1352                         "up": self.pageUp,
1353                         "down": self.pageDown,
1354                         "left": self.pageUp,
1355                         "right": self.pageDown,
1356                 }, -1)
1357
1358                 self["statuspic"] = Pixmap()
1359                 self["divpic"] = Pixmap()
1360                 self["screenshot"] = Pixmap()
1361                 self["closetext"] = Label(_("Close"))
1362                 self["statetext"] = Label()
1363                 self["detailtext"] = ScrollLabel()
1364                 self["author"] = Label()
1365                 self["statuspic"].hide()
1366                 self["screenshot"].hide()
1367                 self["divpic"].hide()
1368
1369                 self.package = self.packageDetails[0]
1370                 if self.package[0].has_key("attributes"):
1371                         self.attributes = self.package[0]["attributes"]
1372                 if self.package[0].has_key("translation"):
1373                         self.translatedAttributes = self.package[0]["translation"]
1374
1375                 self.cmdList = []
1376                 self.oktext = _("\nAfter pressing OK, please wait!")
1377                 self.picload = ePicLoad()
1378                 self.picload.PictureData.get().append(self.paintScreenshotPixmapCB)
1379                 self.onShown.append(self.setWindowTitle)
1380                 self.onLayoutFinish.append(self.setInfos)
1381
1382         def setWindowTitle(self):
1383                 self.setTitle(_("Package details for: " + self.pluginname))
1384
1385         def exit(self):
1386                 self.close(False)
1387
1388         def pageUp(self):
1389                 self["detailtext"].pageUp()
1390
1391         def pageDown(self):
1392                 self["detailtext"].pageDown()
1393
1394         def statusCallback(self, status, progress):
1395                 pass
1396
1397         def setInfos(self):
1398                 if self.translatedAttributes.has_key("name"):
1399                         self.pluginname = self.translatedAttributes["name"]
1400                 elif self.attributes.has_key("name"):
1401                         self.pluginname = self.attributes["name"]
1402                 else:
1403                         self.pluginname = _("unknown")
1404
1405                 if self.translatedAttributes.has_key("author"):
1406                         self.author = self.translatedAttributes["author"]
1407                 elif self.attributes.has_key("author"):
1408                         self.author = self.attributes["author"]
1409                 else:
1410                         self.author = _("unknown")
1411
1412                 if self.translatedAttributes.has_key("description"):
1413                         self.description = self.translatedAttributes["description"]
1414                 elif self.attributes.has_key("description"):
1415                         self.description = self.attributes["description"]
1416                 else:
1417                         self.description = _("No description available.")
1418
1419                 if self.translatedAttributes.has_key("screenshot"):
1420                         self.loadThumbnail(self.translatedAttributes)
1421                 else:
1422                         self.loadThumbnail(self.attributes)
1423
1424                 self["author"].setText(_("Author: ") + self.author)
1425                 self["detailtext"].setText(self.description.strip())
1426                 if self.pluginstate == 'installable':
1427                         self["statetext"].setText(_("Install"))
1428                 else:
1429                         self["statetext"].setText(_("Remove"))
1430
1431         def loadThumbnail(self, entry):
1432                 thumbnailUrl = None
1433                 if entry.has_key("screenshot"):
1434                         thumbnailUrl = entry["screenshot"]
1435                 if thumbnailUrl is not None:
1436                         self.thumbnail = "/tmp/" + thumbnailUrl.split('/')[-1]
1437                         print "[PluginDetails] downloading screenshot " + thumbnailUrl + " to " + self.thumbnail
1438                         client.downloadPage(thumbnailUrl,self.thumbnail).addCallback(self.setThumbnail).addErrback(self.fetchFailed)
1439                 else:
1440                         self.setThumbnail(noScreenshot = True)
1441
1442         def setThumbnail(self, noScreenshot = False):
1443                 if not noScreenshot:
1444                         filename = self.thumbnail
1445                 else:
1446                         filename = resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/noprev.png")
1447
1448                 sc = AVSwitch().getFramebufferScale()
1449                 self.picload.setPara((self["screenshot"].instance.size().width(), self["screenshot"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
1450                 self.picload.startDecode(filename)
1451
1452                 if self.statuspicinstance != None:
1453                         self["statuspic"].instance.setPixmap(self.statuspicinstance.__deref__())
1454                         self["statuspic"].show()
1455                 if self.divpicinstance != None:
1456                         self["divpic"].instance.setPixmap(self.divpicinstance.__deref__())
1457                         self["divpic"].show()
1458
1459         def paintScreenshotPixmapCB(self, picInfo=None):
1460                 ptr = self.picload.getData()
1461                 if ptr != None:
1462                         self["screenshot"].instance.setPixmap(ptr.__deref__())
1463                         self["screenshot"].show()
1464                 else:
1465                         self.setThumbnail(noScreenshot = True)
1466
1467         def go(self):
1468                 if self.attributes.has_key("package"):
1469                         self.packagefiles = self.attributes["package"]
1470                 self.cmdList = []
1471                 if self.pluginstate == 'installed':
1472                         if self.packagefiles:
1473                                 for package in self.packagefiles[:]:
1474                                         self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": package["name"] }))
1475                                         if len(self.cmdList):
1476                                                 self.session.openWithCallback(self.runRemove, MessageBox, _("Do you want to remove the package:\n") + self.pluginname + "\n" + self.oktext)
1477                 else:
1478                         if self.packagefiles:
1479                                 for package in self.packagefiles[:]:
1480                                         self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package["name"] }))
1481                                         if len(self.cmdList):
1482                                                 self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n") + self.pluginname + "\n" + self.oktext)
1483
1484         def runUpgrade(self, result):
1485                 if result:
1486                         self.session.openWithCallback(self.runUpgradeFinished, Ipkg, cmdList = self.cmdList)
1487
1488         def runUpgradeFinished(self):
1489                 self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Installation finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1490
1491         def UpgradeReboot(self, result):
1492                 if result is None:
1493                         return
1494                 if result is False:
1495                         self.close(True)
1496                 if result:
1497                         quitMainloop(3)
1498
1499         def runRemove(self, result):
1500                 if result:
1501                         self.session.openWithCallback(self.runRemoveFinished, Ipkg, cmdList = self.cmdList)
1502
1503         def runRemoveFinished(self):
1504                 self.session.openWithCallback(self.RemoveReboot, MessageBox, _("Remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1505
1506         def RemoveReboot(self, result):
1507                 if result is None:
1508                         return
1509                 if result is False:
1510                         self.close(True)
1511                 if result:
1512                         quitMainloop(3)
1513
1514         def reloadPluginlist(self):
1515                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
1516
1517         def fetchFailed(self,string):
1518                 self.setThumbnail(noScreenshot = True)
1519                 print "[PluginDetails] fetch failed " + string.getErrorMessage()
1520
1521
1522 class UpdatePlugin(Screen):
1523         skin = """
1524                 <screen position="100,100" size="550,200" title="Software Update..." >
1525                         <widget name="activityslider" position="0,0" size="550,5"  />
1526                         <widget name="slider" position="0,100" size="550,30"  />
1527                         <widget name="package" position="10,30" size="540,20" font="Regular;18"/>
1528                         <widget name="status" position="10,60" size="540,45" font="Regular;18"/>
1529                 </screen>"""
1530                 
1531         def __init__(self, session, args = None):
1532                 self.skin = UpdatePlugin.skin
1533                 Screen.__init__(self, session)
1534                 
1535                 self.sliderPackages = { "dreambox-dvb-modules": 1, "enigma2": 2, "tuxbox-image-info": 3 }
1536                 
1537                 self.slider = Slider(0, 4)
1538                 self["slider"] = self.slider
1539                 self.activityslider = Slider(0, 100)
1540                 self["activityslider"] = self.activityslider
1541                 self.status = Label(_("Upgrading Dreambox... Please wait"))
1542                 self["status"] = self.status
1543                 self.package = Label()
1544                 self["package"] = self.package
1545                 
1546                 self.packages = 0
1547                 self.error = 0
1548                 
1549                 self.activity = 0
1550                 self.activityTimer = eTimer()
1551                 self.activityTimer.callback.append(self.doActivityTimer)
1552                 self.activityTimer.start(100, False)
1553                                 
1554                 self.ipkg = IpkgComponent()
1555                 self.ipkg.addCallback(self.ipkgCallback)
1556                 
1557                 self.updating = True
1558                 self.package.setText(_("Package list update"))
1559                 self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
1560                         
1561                 self["actions"] = ActionMap(["WizardActions"], 
1562                 {
1563                         "ok": self.exit,
1564                         "back": self.exit
1565                 }, -1)
1566                 
1567         def doActivityTimer(self):
1568                 self.activity += 1
1569                 if self.activity == 100:
1570                         self.activity = 0
1571                 self.activityslider.setValue(self.activity)
1572                 
1573         def ipkgCallback(self, event, param):
1574                 if event == IpkgComponent.EVENT_DOWNLOAD:
1575                         self.status.setText(_("Downloading"))
1576                 elif event == IpkgComponent.EVENT_UPGRADE:
1577                         if self.sliderPackages.has_key(param):
1578                                 self.slider.setValue(self.sliderPackages[param])
1579                         self.package.setText(param)
1580                         self.status.setText(_("Upgrading"))
1581                         self.packages += 1
1582                 elif event == IpkgComponent.EVENT_INSTALL:
1583                         self.package.setText(param)
1584                         self.status.setText(_("Installing"))
1585                         self.packages += 1
1586                 elif event == IpkgComponent.EVENT_CONFIGURING:
1587                         self.package.setText(param)
1588                         self.status.setText(_("Configuring"))
1589                 elif event == IpkgComponent.EVENT_MODIFIED:
1590                         self.session.openWithCallback(
1591                                 self.modificationCallback,
1592                                 MessageBox,
1593                                 _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param)
1594                         )
1595                 elif event == IpkgComponent.EVENT_ERROR:
1596                         self.error += 1
1597                 elif event == IpkgComponent.EVENT_DONE:
1598                         if self.updating:
1599                                 self.updating = False
1600                                 self.ipkg.startCmd(IpkgComponent.CMD_UPGRADE, args = {'test_only': False})
1601                         elif self.error == 0:
1602                                 self.slider.setValue(4)
1603                                 
1604                                 self.activityTimer.stop()
1605                                 self.activityslider.setValue(0)
1606                                 
1607                                 self.package.setText("")
1608                                 self.status.setText(_("Done - Installed or upgraded %d packages") % self.packages)
1609                         else:
1610                                 self.activityTimer.stop()
1611                                 self.activityslider.setValue(0)
1612                                 error = _("your dreambox might be unusable now. Please consult the manual for further assistance before rebooting your dreambox.")
1613                                 if self.packages == 0:
1614                                         error = _("No packages were upgraded yet. So you can check your network and try again.")
1615                                 if self.updating:
1616                                         error = _("Your dreambox isn't connected to the internet properly. Please check it and try again.")
1617                                 self.status.setText(_("Error") +  " - " + error)
1618                 #print event, "-", param
1619                 pass
1620
1621         def modificationCallback(self, res):
1622                 self.ipkg.write(res and "N" or "Y")
1623
1624         def exit(self):
1625                 if not self.ipkg.isRunning():
1626                         if self.packages != 0 and self.error == 0:
1627                                 self.session.openWithCallback(self.exitAnswer, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"))
1628                         else:
1629                                 self.close()
1630                         
1631         def exitAnswer(self, result):
1632                 if result is not None and result:
1633                         quitMainloop(2)
1634                 self.close()
1635
1636
1637 class IpkgInstaller(Screen):
1638         skin = """
1639                 <screen position="100,100" size="550,400" title="..." >
1640                         <widget name="red" halign="center" valign="center" position="0,0" size="140,60" backgroundColor="red" font="Regular;21" />
1641                         <widget name="green" halign="center" valign="center" position="140,0" text="Install selected" size="140,60" backgroundColor="green" font="Regular;21" />
1642                         <widget name="yellow" halign="center" valign="center" position="280,0" size="140,60" backgroundColor="yellow" font="Regular;21" />
1643                         <widget name="blue" halign="center" valign="center" position="420,0" size="140,60" backgroundColor="blue" font="Regular;21" />
1644                         <widget name="list" position="0,60" size="550,360" />
1645                 </screen>
1646                 """
1647         
1648         def __init__(self, session, list):
1649                 self.skin = IpkgInstaller.skin
1650                 Screen.__init__(self, session)
1651
1652                 self.list = SelectionList()
1653                 self["list"] = self.list
1654                 for listindex in range(len(list)):
1655                         self.list.addSelection(list[listindex], list[listindex], listindex, True)
1656
1657                 self["red"] = Label()
1658                 self["green"] = Label()
1659                 self["yellow"] = Label()
1660                 self["blue"] = Label()
1661                 
1662                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], 
1663                 {
1664                         "ok": self.list.toggleSelection, 
1665                         "cancel": self.close, 
1666                         "green": self.install
1667                 }, -1)
1668                 
1669         def install(self):
1670                 list = self.list.getSelectionsList()
1671                 cmdList = []
1672                 for item in list:
1673                         cmdList.append((IpkgComponent.CMD_INSTALL, { "package": item[1] }))
1674                 self.session.open(Ipkg, cmdList = cmdList)
1675
1676 def filescan_open(list, session, **kwargs):
1677         filelist = [x.path for x in list]
1678         session.open(IpkgInstaller, filelist) # list
1679
1680 def filescan(**kwargs):
1681         from Components.Scanner import Scanner, ScanPath
1682         return \
1683                 Scanner(mimetypes = ["application/x-debian-package"], 
1684                         paths_to_scan = 
1685                                 [
1686                                         ScanPath(path = "ipk", with_subdirs = True), 
1687                                         ScanPath(path = "", with_subdirs = False), 
1688                                 ], 
1689                         name = "Ipkg", 
1690                         description = _("Install software updates..."),
1691                         openfnc = filescan_open, )
1692
1693
1694
1695 def UpgradeMain(session, **kwargs):
1696         session.open(UpdatePluginMenu)
1697
1698 def startSetup(menuid):
1699         if menuid != "setup": 
1700                 return [ ]
1701         return [(_("Software manager"), UpgradeMain, "software_manager", 50)]
1702
1703 def Plugins(path, **kwargs):
1704         global plugin_path
1705         plugin_path = path
1706         list = [
1707                 PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup), 
1708                 PluginDescriptor(name=_("Ipkg"), where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)
1709         ]
1710         if config.usage.setup_level.index >= 2: # expert+       
1711                 list.append(PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=UpgradeMain))     
1712         return list