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