Merge branch 'bug_537_vobsub' of git.opendreambox.org:/git/enigma2 into bug_537_vobsub
[vuplus_dvbapp] / lib / python / Plugins / SystemPlugins / SoftwareManager / plugin.py
1 from Plugins.Plugin import PluginDescriptor
2 from Screens.Console import Console
3 from Screens.ChoiceBox import ChoiceBox
4 from Screens.MessageBox import MessageBox
5 from Screens.Screen import Screen
6 from Screens.Ipkg import Ipkg
7 from Components.ActionMap import ActionMap, NumberActionMap
8 from Components.Input import Input
9 from Components.Ipkg import IpkgComponent
10 from Components.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                 self.translatedAttributes = None
1108                 DreamInfoHandler.__init__(self, self.statusCallback, blocking = False, language = self.language)
1109                 self.directory = resolveFilename(SCOPE_METADIR)
1110                 if packagedata:
1111                         self.pluginname = packagedata[0]
1112                         self.details = packagedata[1]
1113                         self.pluginstate = packagedata[4]
1114                         self.statuspicinstance = packagedata[5]
1115                         self.divpicinstance = packagedata[6]
1116                         self.fillPackageDetails(self.details)
1117
1118                 self.thumbnail = ""
1119
1120                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
1121                 {
1122                         "back": self.exit,
1123                         "red": self.exit,
1124                         "green": self.go,
1125                         "up": self.pageUp,
1126                         "down": self.pageDown,
1127                         "left": self.pageUp,
1128                         "right": self.pageDown,
1129                 }, -1)
1130
1131                 self["key_red"] = StaticText(_("Close"))
1132                 self["key_green"] = StaticText("")
1133                 self["author"] = StaticText()
1134                 self["statuspic"] = Pixmap()
1135                 self["divpic"] = Pixmap()
1136                 self["screenshot"] = Pixmap()
1137                 self["detailtext"] = ScrollLabel()
1138
1139                 self["statuspic"].hide()
1140                 self["screenshot"].hide()
1141                 self["divpic"].hide()
1142
1143                 self.package = self.packageDetails[0]
1144                 if self.package[0].has_key("attributes"):
1145                         self.attributes = self.package[0]["attributes"]
1146                 if self.package[0].has_key("translation"):
1147                         self.translatedAttributes = self.package[0]["translation"]
1148
1149                 self.cmdList = []
1150                 self.oktext = _("\nAfter pressing OK, please wait!")
1151                 self.picload = ePicLoad()
1152                 self.picload.PictureData.get().append(self.paintScreenshotPixmapCB)
1153                 self.onShown.append(self.setWindowTitle)
1154                 self.onLayoutFinish.append(self.setInfos)
1155
1156         def setWindowTitle(self):
1157                 self.setTitle(_("Details for extension: " + self.pluginname))
1158
1159         def exit(self):
1160                 self.close(False)
1161
1162         def pageUp(self):
1163                 self["detailtext"].pageUp()
1164
1165         def pageDown(self):
1166                 self["detailtext"].pageDown()
1167
1168         def statusCallback(self, status, progress):
1169                 pass
1170
1171         def setInfos(self):
1172                 if self.translatedAttributes.has_key("name"):
1173                         self.pluginname = self.translatedAttributes["name"]
1174                 elif self.attributes.has_key("name"):
1175                         self.pluginname = self.attributes["name"]
1176                 else:
1177                         self.pluginname = _("unknown")
1178
1179                 if self.translatedAttributes.has_key("author"):
1180                         self.author = self.translatedAttributes["author"]
1181                 elif self.attributes.has_key("author"):
1182                         self.author = self.attributes["author"]
1183                 else:
1184                         self.author = _("unknown")
1185
1186                 if self.translatedAttributes.has_key("description"):
1187                         self.description = self.translatedAttributes["description"]
1188                 elif self.attributes.has_key("description"):
1189                         self.description = self.attributes["description"]
1190                 else:
1191                         self.description = _("No description available.")
1192
1193                 if self.translatedAttributes.has_key("screenshot"):
1194                         self.loadThumbnail(self.translatedAttributes)
1195                 else:
1196                         self.loadThumbnail(self.attributes)
1197
1198                 self["author"].setText(_("Author: ") + self.author)
1199                 self["detailtext"].setText(self.description.strip())
1200                 if self.pluginstate in ('installable', 'install'):
1201                         self["key_green"].setText(_("Install"))
1202                 else:
1203                         self["key_green"].setText(_("Remove"))
1204
1205         def loadThumbnail(self, entry):
1206                 thumbnailUrl = None
1207                 if entry.has_key("screenshot"):
1208                         thumbnailUrl = entry["screenshot"]
1209                 if thumbnailUrl is not None:
1210                         self.thumbnail = "/tmp/" + thumbnailUrl.split('/')[-1]
1211                         print "[PluginDetails] downloading screenshot " + thumbnailUrl + " to " + self.thumbnail
1212                         client.downloadPage(thumbnailUrl,self.thumbnail).addCallback(self.setThumbnail).addErrback(self.fetchFailed)
1213                 else:
1214                         self.setThumbnail(noScreenshot = True)
1215
1216         def setThumbnail(self, noScreenshot = False):
1217                 if not noScreenshot:
1218                         filename = self.thumbnail
1219                 else:
1220                         filename = resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/noprev.png")
1221
1222                 sc = AVSwitch().getFramebufferScale()
1223                 self.picload.setPara((self["screenshot"].instance.size().width(), self["screenshot"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
1224                 self.picload.startDecode(filename)
1225
1226                 if self.statuspicinstance != None:
1227                         self["statuspic"].instance.setPixmap(self.statuspicinstance.__deref__())
1228                         self["statuspic"].show()
1229                 if self.divpicinstance != None:
1230                         self["divpic"].instance.setPixmap(self.divpicinstance.__deref__())
1231                         self["divpic"].show()
1232
1233         def paintScreenshotPixmapCB(self, picInfo=None):
1234                 ptr = self.picload.getData()
1235                 if ptr != None:
1236                         self["screenshot"].instance.setPixmap(ptr.__deref__())
1237                         self["screenshot"].show()
1238                 else:
1239                         self.setThumbnail(noScreenshot = True)
1240
1241         def go(self):
1242                 if self.attributes.has_key("package"):
1243                         self.packagefiles = self.attributes["package"]
1244                 self.cmdList = []
1245                 if self.pluginstate in ('installed', 'remove'):
1246                         if self.packagefiles:
1247                                 for package in self.packagefiles[:]:
1248                                         self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": package["name"] }))
1249                                         if len(self.cmdList):
1250                                                 self.session.openWithCallback(self.runRemove, MessageBox, _("Do you want to remove the package:\n") + self.pluginname + "\n" + self.oktext)
1251                 else:
1252                         if self.packagefiles:
1253                                 for package in self.packagefiles[:]:
1254                                         self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package["name"] }))
1255                                         if len(self.cmdList):
1256                                                 self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n") + self.pluginname + "\n" + self.oktext)
1257
1258         def runUpgrade(self, result):
1259                 if result:
1260                         self.session.openWithCallback(self.runUpgradeFinished, Ipkg, cmdList = self.cmdList)
1261
1262         def runUpgradeFinished(self):
1263                 self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Installation finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1264
1265         def UpgradeReboot(self, result):
1266                 if result is None:
1267                         return
1268                 if result is False:
1269                         self.close(True)
1270                 if result:
1271                         quitMainloop(3)
1272
1273         def runRemove(self, result):
1274                 if result:
1275                         self.session.openWithCallback(self.runRemoveFinished, Ipkg, cmdList = self.cmdList)
1276
1277         def runRemoveFinished(self):
1278                 self.session.openWithCallback(self.RemoveReboot, MessageBox, _("Remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1279
1280         def RemoveReboot(self, result):
1281                 if result is None:
1282                         return
1283                 if result is False:
1284                         self.close(True)
1285                 if result:
1286                         quitMainloop(3)
1287
1288         def reloadPluginlist(self):
1289                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
1290
1291         def fetchFailed(self,string):
1292                 self.setThumbnail(noScreenshot = True)
1293                 print "[PluginDetails] fetch failed " + string.getErrorMessage()
1294
1295
1296 class UpdatePlugin(Screen):
1297         skin = """
1298                 <screen name="UpdatePlugin" position="center,center" size="550,300" title="Software update" >
1299                         <widget name="activityslider" position="0,0" size="550,5"  />
1300                         <widget name="slider" position="0,150" size="550,30"  />
1301                         <widget source="package" render="Label" position="10,30" size="540,20" font="Regular;18" halign="center" valign="center" backgroundColor="#25062748" transparent="1" />
1302                         <widget source="status" render="Label" position="10,60" size="540,45" font="Regular;20" halign="center" valign="center" backgroundColor="#25062748" transparent="1" />
1303                 </screen>"""
1304
1305         def __init__(self, session, args = None):
1306                 Screen.__init__(self, session)
1307
1308                 self.sliderPackages = { "dreambox-dvb-modules": 1, "enigma2": 2, "tuxbox-image-info": 3 }
1309
1310                 self.slider = Slider(0, 4)
1311                 self["slider"] = self.slider
1312                 self.activityslider = Slider(0, 100)
1313                 self["activityslider"] = self.activityslider
1314                 self.status = StaticText(_("Upgrading Dreambox... Please wait"))
1315                 self["status"] = self.status
1316                 self.package = StaticText()
1317                 self["package"] = self.package
1318                 self.oktext = _("Press OK on your remote control to continue.")
1319
1320                 self.packages = 0
1321                 self.error = 0
1322                 self.processed_packages = []
1323
1324                 self.activity = 0
1325                 self.activityTimer = eTimer()
1326                 self.activityTimer.callback.append(self.doActivityTimer)
1327                 self.activityTimer.start(100, False)
1328
1329                 self.ipkg = IpkgComponent()
1330                 self.ipkg.addCallback(self.ipkgCallback)
1331
1332                 self.updating = True
1333                 self.package.setText(_("Package list update"))
1334                 self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
1335
1336                 self["actions"] = ActionMap(["WizardActions"], 
1337                 {
1338                         "ok": self.exit,
1339                         "back": self.exit
1340                 }, -1)
1341
1342         def doActivityTimer(self):
1343                 self.activity += 1
1344                 if self.activity == 100:
1345                         self.activity = 0
1346                 self.activityslider.setValue(self.activity)
1347
1348         def ipkgCallback(self, event, param):
1349                 if event == IpkgComponent.EVENT_DOWNLOAD:
1350                         self.status.setText(_("Downloading"))
1351                 elif event == IpkgComponent.EVENT_UPGRADE:
1352                         if self.sliderPackages.has_key(param):
1353                                 self.slider.setValue(self.sliderPackages[param])
1354                         self.package.setText(param)
1355                         self.status.setText(_("Upgrading"))
1356                         if not param in self.processed_packages:
1357                                 self.processed_packages.append(param)
1358                                 self.packages += 1
1359                 elif event == IpkgComponent.EVENT_INSTALL:
1360                         self.package.setText(param)
1361                         self.status.setText(_("Installing"))
1362                         if not param in self.processed_packages:
1363                                 self.processed_packages.append(param)
1364                                 self.packages += 1
1365                 elif event == IpkgComponent.EVENT_REMOVE:
1366                         self.package.setText(param)
1367                         self.status.setText(_("Removing"))
1368                         if not param in self.processed_packages:
1369                                 self.processed_packages.append(param)
1370                                 self.packages += 1
1371                 elif event == IpkgComponent.EVENT_CONFIGURING:
1372                         self.package.setText(param)
1373                         self.status.setText(_("Configuring"))
1374                         
1375                 elif event == IpkgComponent.EVENT_MODIFIED:
1376                         if config.plugins.SoftwareManager.overwriteConfigFiles.value in ("N", "Y"):
1377                                 self.ipkg.write(True and config.plugins.SoftwareManager.overwriteConfigFiles.value)
1378                         else:
1379                                 self.session.openWithCallback(
1380                                         self.modificationCallback,
1381                                         MessageBox,
1382                                         _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param)
1383                                 )
1384                 elif event == IpkgComponent.EVENT_ERROR:
1385                         self.error += 1
1386                 elif event == IpkgComponent.EVENT_DONE:
1387                         if self.updating:
1388                                 self.updating = False
1389                                 self.ipkg.startCmd(IpkgComponent.CMD_UPGRADE, args = {'test_only': False})
1390                         elif self.error == 0:
1391                                 self.slider.setValue(4)
1392                                 
1393                                 self.activityTimer.stop()
1394                                 self.activityslider.setValue(0)
1395                                 
1396                                 self.package.setText("")
1397                                 self.status.setText(_("Done - Installed or upgraded %d packages") % self.packages + "\n\n" + self.oktext)
1398                         else:
1399                                 self.activityTimer.stop()
1400                                 self.activityslider.setValue(0)
1401                                 error = _("your dreambox might be unusable now. Please consult the manual for further assistance before rebooting your dreambox.")
1402                                 if self.packages == 0:
1403                                         error = _("No packages were upgraded yet. So you can check your network and try again.")
1404                                 if self.updating:
1405                                         error = _("Your dreambox isn't connected to the internet properly. Please check it and try again.")
1406                                 self.status.setText(_("Error") +  " - " + error)
1407                 #print event, "-", param
1408                 pass
1409
1410         def modificationCallback(self, res):
1411                 self.ipkg.write(res and "N" or "Y")
1412
1413         def exit(self):
1414                 if not self.ipkg.isRunning():
1415                         if self.packages != 0 and self.error == 0:
1416                                 self.session.openWithCallback(self.exitAnswer, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"))
1417                         else:
1418                                 self.close()
1419
1420         def exitAnswer(self, result):
1421                 if result is not None and result:
1422                         quitMainloop(2)
1423                 self.close()
1424
1425
1426
1427 class IPKGMenu(Screen):
1428         skin = """
1429                 <screen name="IPKGMenu" position="center,center" size="560,400" title="Select upgrade source to edit." >
1430                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
1431                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
1432                         <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" />
1433                         <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" />
1434                         <widget name="filelist" position="5,50" size="550,340" scrollbarMode="showOnDemand" />
1435                 </screen>"""
1436
1437         def __init__(self, session, plugin_path):
1438                 Screen.__init__(self, session)
1439                 self.skin_path = plugin_path
1440                 
1441                 self["key_red"] = StaticText(_("Close"))
1442                 self["key_green"] = StaticText(_("Edit"))
1443
1444                 self.sel = []
1445                 self.val = []
1446                 self.entry = False
1447                 self.exe = False
1448                 
1449                 self.path = ""
1450
1451                 self["actions"] = NumberActionMap(["SetupActions"],
1452                 {
1453                         "ok": self.KeyOk,
1454                         "cancel": self.keyCancel
1455                 }, -1)
1456
1457                 self["shortcuts"] = ActionMap(["ShortcutActions"],
1458                 {
1459                         "red": self.keyCancel,
1460                         "green": self.KeyOk,
1461                 })
1462                 self.flist = []
1463                 self["filelist"] = MenuList(self.flist)
1464                 self.fill_list()
1465                 self.onLayoutFinish.append(self.layoutFinished)
1466
1467         def layoutFinished(self):
1468                 self.setWindowTitle()
1469
1470         def setWindowTitle(self):
1471                 self.setTitle(_("Select upgrade source to edit."))
1472
1473         def fill_list(self):
1474                 self.flist = []
1475                 self.path = '/etc/ipkg/'
1476                 if (os_path.exists(self.path) == False):
1477                         self.entry = False
1478                         return
1479                 for file in listdir(self.path):
1480                         if (file.endswith(".conf")):
1481                                 if file != 'arch.conf':
1482                                         self.flist.append((file))
1483                                         self.entry = True
1484                                         self["filelist"].l.setList(self.flist)
1485
1486         def KeyOk(self):
1487                 if (self.exe == False) and (self.entry == True):
1488                         self.sel = self["filelist"].getCurrent()
1489                         self.val = self.path + self.sel
1490                         self.session.open(IPKGSource, self.val)
1491
1492         def keyCancel(self):
1493                 self.close()
1494
1495         def Exit(self):
1496                 self.close()
1497
1498
1499 class IPKGSource(Screen):
1500         skin = """
1501                 <screen name="IPKGSource" position="center,center" size="560,80" title="Edit upgrade source url." >
1502                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
1503                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
1504                         <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" />
1505                         <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" />
1506                         <widget name="text" position="5,50" size="550,25" font="Regular;20" backgroundColor="background" foregroundColor="#cccccc" />
1507                 </screen>"""
1508
1509         def __init__(self, session, configfile = None):
1510                 Screen.__init__(self, session)
1511                 self.session = session
1512                 self.configfile = configfile
1513                 text = ""
1514                 if self.configfile:
1515                         try:
1516                                 fp = file(configfile, 'r')
1517                                 sources = fp.readlines()
1518                                 if sources:
1519                                         text = sources[0]
1520                                 fp.close()
1521                         except IOError:
1522                                 pass
1523
1524                 desk = getDesktop(0)
1525                 x= int(desk.size().width())
1526                 y= int(desk.size().height())
1527
1528                 self["key_red"] = StaticText(_("Cancel"))
1529                 self["key_green"] = StaticText(_("Save"))
1530
1531                 if (y>=720):
1532                         self["text"] = Input(text, maxSize=False, type=Input.TEXT)
1533                 else:
1534                         self["text"] = Input(text, maxSize=False, visible_width = 55, type=Input.TEXT)
1535
1536                 self["actions"] = NumberActionMap(["WizardActions", "InputActions", "TextEntryActions", "KeyboardInputActions","ShortcutActions"], 
1537                 {
1538                         "ok": self.go,
1539                         "back": self.close,
1540                         "red": self.close,
1541                         "green": self.go,
1542                         "left": self.keyLeft,
1543                         "right": self.keyRight,
1544                         "home": self.keyHome,
1545                         "end": self.keyEnd,
1546                         "deleteForward": self.keyDeleteForward,
1547                         "deleteBackward": self.keyDeleteBackward,
1548                         "1": self.keyNumberGlobal,
1549                         "2": self.keyNumberGlobal,
1550                         "3": self.keyNumberGlobal,
1551                         "4": self.keyNumberGlobal,
1552                         "5": self.keyNumberGlobal,
1553                         "6": self.keyNumberGlobal,
1554                         "7": self.keyNumberGlobal,
1555                         "8": self.keyNumberGlobal,
1556                         "9": self.keyNumberGlobal,
1557                         "0": self.keyNumberGlobal
1558                 }, -1)
1559
1560                 self.onLayoutFinish.append(self.layoutFinished)
1561
1562         def layoutFinished(self):
1563                 self.setWindowTitle()
1564                 self["text"].right()
1565
1566         def setWindowTitle(self):
1567                 self.setTitle(_("Edit upgrade source url."))
1568
1569         def go(self):
1570                 text = self["text"].getText()
1571                 if text:
1572                         fp = file(self.configfile, 'w')
1573                         fp.write(text)
1574                         fp.write("\n")
1575                         fp.close()
1576                 self.close()
1577
1578         def keyLeft(self):
1579                 self["text"].left()
1580         
1581         def keyRight(self):
1582                 self["text"].right()
1583         
1584         def keyHome(self):
1585                 self["text"].home()
1586         
1587         def keyEnd(self):
1588                 self["text"].end()
1589         
1590         def keyDeleteForward(self):
1591                 self["text"].delete()
1592         
1593         def keyDeleteBackward(self):
1594                 self["text"].deleteBackward()
1595         
1596         def keyNumberGlobal(self, number):
1597                 self["text"].number(number)
1598
1599
1600 class PacketManager(Screen, NumericalTextInput):
1601         skin = """
1602                 <screen name="PacketManager" position="center,center" size="530,420" title="Packet manager" >
1603                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
1604                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
1605                         <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" />
1606                         <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" />
1607                         <widget source="list" render="Listbox" position="5,50" size="520,365" scrollbarMode="showOnDemand">
1608                                 <convert type="TemplatedMultiContent">
1609                                         {"template": [
1610                                                         MultiContentEntryText(pos = (5, 1), size = (440, 28), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name
1611                                                         MultiContentEntryText(pos = (5, 26), size = (440, 20), font=1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the description
1612                                                         MultiContentEntryPixmapAlphaTest(pos = (445, 2), size = (48, 48), png = 4), # index 4 is the status pixmap
1613                                                         MultiContentEntryPixmapAlphaTest(pos = (5, 50), size = (510, 2), png = 5), # index 4 is the div pixmap
1614                                                 ],
1615                                         "fonts": [gFont("Regular", 22),gFont("Regular", 14)],
1616                                         "itemHeight": 52
1617                                         }
1618                                 </convert>
1619                         </widget>
1620                 </screen>"""
1621                 
1622         def __init__(self, session, plugin_path, args = None):
1623                 Screen.__init__(self, session)
1624                 NumericalTextInput.__init__(self)
1625                 self.session = session
1626                 self.skin_path = plugin_path
1627
1628                 self.setUseableChars(u'1234567890abcdefghijklmnopqrstuvwxyz')
1629
1630                 self["shortcuts"] = NumberActionMap(["ShortcutActions", "WizardActions", "NumberActions", "InputActions", "InputAsciiActions", "KeyboardInputActions" ],
1631                 {
1632                         "ok": self.go,
1633                         "back": self.exit,
1634                         "red": self.exit,
1635                         "green": self.reload,
1636                         "gotAsciiCode": self.keyGotAscii,
1637                         "1": self.keyNumberGlobal,
1638                         "2": self.keyNumberGlobal,
1639                         "3": self.keyNumberGlobal,
1640                         "4": self.keyNumberGlobal,
1641                         "5": self.keyNumberGlobal,
1642                         "6": self.keyNumberGlobal,
1643                         "7": self.keyNumberGlobal,
1644                         "8": self.keyNumberGlobal,
1645                         "9": self.keyNumberGlobal,
1646                         "0": self.keyNumberGlobal
1647                 }, -1)
1648                 
1649                 self.list = []
1650                 self.statuslist = []
1651                 self["list"] = List(self.list)
1652                 self["key_red"] = StaticText(_("Close"))
1653                 self["key_green"] = StaticText(_("Reload"))
1654
1655                 self.list_updating = True
1656                 self.packetlist = []
1657                 self.installed_packetlist = {}
1658                 self.Console = Console()
1659                 self.cmdList = []
1660                 self.cachelist = []
1661                 self.cache_ttl = 86400  #600 is default, 0 disables, Seconds cache is considered valid (24h should be ok for caching ipkgs)
1662                 self.cache_file = '/usr/lib/enigma2/python/Plugins/SystemPlugins/SoftwareManager/packetmanager.cache' #Path to cache directory   
1663                 self.oktext = _("\nAfter pressing OK, please wait!")
1664                 self.unwanted_extensions = ('-dbg', '-dev', '-doc', 'busybox')
1665
1666                 self.ipkg = IpkgComponent()
1667                 self.ipkg.addCallback(self.ipkgCallback)
1668                 self.onShown.append(self.setWindowTitle)
1669                 self.onLayoutFinish.append(self.rebuildList)
1670
1671                 rcinput = eRCInput.getInstance()
1672                 rcinput.setKeyboardMode(rcinput.kmAscii)                
1673
1674         def keyNumberGlobal(self, val):
1675                 key = self.getKey(val)
1676                 if key is not None:
1677                         keyvalue = key.encode("utf-8")
1678                         if len(keyvalue) == 1:
1679                                 self.setNextIdx(keyvalue[0])
1680                 
1681         def keyGotAscii(self):
1682                 keyvalue = unichr(getPrevAsciiCode()).encode("utf-8")
1683                 if len(keyvalue) == 1:
1684                         self.setNextIdx(keyvalue[0])
1685                 
1686         def setNextIdx(self,char):
1687                 if char in ("0", "1", "a"):
1688                         self["list"].setIndex(0)
1689                 else:
1690                         idx = self.getNextIdx(char)
1691                         if idx and idx <= self["list"].count:
1692                                 self["list"].setIndex(idx)
1693
1694         def getNextIdx(self,char):
1695                 idx = 0
1696                 for i in self["list"].list:
1697                         if i[0][0] == char:
1698                                 return idx
1699                         idx += 1
1700
1701         def exit(self):
1702                 self.ipkg.stop()
1703                 if self.Console is not None:
1704                         if len(self.Console.appContainers):
1705                                 for name in self.Console.appContainers.keys():
1706                                         self.Console.kill(name)
1707                 rcinput = eRCInput.getInstance()
1708                 rcinput.setKeyboardMode(rcinput.kmNone)
1709                 self.close()
1710
1711         def reload(self):
1712                 if (os_path.exists(self.cache_file) == True):
1713                         remove(self.cache_file)
1714                         self.list_updating = True
1715                         self.rebuildList()
1716                         
1717         def setWindowTitle(self):
1718                 self.setTitle(_("Packet manager"))
1719
1720         def setStatus(self,status = None):
1721                 if status:
1722                         self.statuslist = []
1723                         divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/div-h.png"))
1724                         if status == 'update':
1725                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/upgrade.png"))
1726                                 self.statuslist.append(( _("Package list update"), '', _("Trying to download a new packetlist. Please wait..." ),'',statuspng, divpng ))
1727                                 self['list'].setList(self.statuslist)   
1728                         elif status == 'error':
1729                                 statuspng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/remove.png"))
1730                                 self.statuslist.append(( _("Error"), '', _("There was an error downloading the packetlist. Please try again." ),'',statuspng, divpng ))
1731                                 self['list'].setList(self.statuslist)                           
1732
1733         def rebuildList(self):
1734                 self.setStatus('update')
1735                 self.inv_cache = 0
1736                 self.vc = valid_cache(self.cache_file, self.cache_ttl)
1737                 if self.cache_ttl > 0 and self.vc != 0:
1738                         try:
1739                                 self.buildPacketList()
1740                         except:
1741                                 self.inv_cache = 1
1742                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
1743                         self.run = 0
1744                         self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
1745
1746         def go(self, returnValue = None):
1747                 cur = self["list"].getCurrent()
1748                 if cur:
1749                         status = cur[3]
1750                         package = cur[0]
1751                         self.cmdList = []
1752                         if status == 'installed':
1753                                 self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": package }))
1754                                 if len(self.cmdList):
1755                                         self.session.openWithCallback(self.runRemove, MessageBox, _("Do you want to remove the package:\n") + package + "\n" + self.oktext)
1756                         elif status == 'upgradeable':
1757                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package }))
1758                                 if len(self.cmdList):
1759                                         self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to upgrade the package:\n") + package + "\n" + self.oktext)
1760                         elif status == "installable":
1761                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package }))
1762                                 if len(self.cmdList):
1763                                         self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n") + package + "\n" + self.oktext)
1764
1765         def runRemove(self, result):
1766                 if result:
1767                         self.session.openWithCallback(self.runRemoveFinished, Ipkg, cmdList = self.cmdList)
1768
1769         def runRemoveFinished(self):
1770                 self.session.openWithCallback(self.RemoveReboot, MessageBox, _("Remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1771
1772         def RemoveReboot(self, result):
1773                 if result is None:
1774                         return
1775                 if result is False:
1776                         cur = self["list"].getCurrent()
1777                         if cur:
1778                                 item = self['list'].getIndex()
1779                                 self.list[item] = self.buildEntryComponent(cur[0], cur[1], cur[2], 'installable')
1780                                 self.cachelist[item] = [cur[0], cur[1], cur[2], 'installable']
1781                                 self['list'].setList(self.list)
1782                                 write_cache(self.cache_file, self.cachelist)
1783                                 self.reloadPluginlist()
1784                 if result:
1785                         quitMainloop(3)
1786
1787         def runUpgrade(self, result):
1788                 if result:
1789                         self.session.openWithCallback(self.runUpgradeFinished, Ipkg, cmdList = self.cmdList)
1790
1791         def runUpgradeFinished(self):
1792                 self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1793                 
1794         def UpgradeReboot(self, result):
1795                 if result is None:
1796                         return
1797                 if result is False:
1798                         cur = self["list"].getCurrent()
1799                         if cur:
1800                                 item = self['list'].getIndex()
1801                                 self.list[item] = self.buildEntryComponent(cur[0], cur[1], cur[2], 'installed')
1802                                 self.cachelist[item] = [cur[0], cur[1], cur[2], 'installed']
1803                                 self['list'].setList(self.list)
1804                                 write_cache(self.cache_file, self.cachelist)
1805                                 self.reloadPluginlist()
1806                 if result:
1807                         quitMainloop(3)
1808
1809         def ipkgCallback(self, event, param):
1810                 if event == IpkgComponent.EVENT_ERROR:
1811                         self.list_updating = False
1812                         self.setStatus('error')
1813                 elif event == IpkgComponent.EVENT_DONE:
1814                         if self.list_updating:
1815                                 self.list_updating = False
1816                                 if not self.Console:
1817                                         self.Console = Console()
1818                                 cmd = "ipkg list"
1819                                 self.Console.ePopen(cmd, self.IpkgList_Finished)
1820                 #print event, "-", param
1821                 pass
1822
1823         def IpkgList_Finished(self, result, retval, extra_args = None):
1824                 if result:
1825                         self.packetlist = []
1826                         for x in result.splitlines():
1827                                 tokens = x.split(' - ')   #self.blacklisted_packages
1828                                 name = tokens[0].strip()
1829                                 if not any(name.endswith(x) for x in self.unwanted_extensions):
1830                                         l = len(tokens)
1831                                         version = l > 1 and tokens[1].strip() or ""
1832                                         descr = l > 2 and tokens[2].strip() or ""
1833                                         self.packetlist.append([name, version, descr])
1834                 if not self.Console:
1835                         self.Console = Console()
1836                 cmd = "ipkg list_installed"
1837                 self.Console.ePopen(cmd, self.IpkgListInstalled_Finished)
1838
1839         def IpkgListInstalled_Finished(self, result, retval, extra_args = None):
1840                 if result:
1841                         self.installed_packetlist = {}
1842                         for x in result.splitlines():
1843                                 tokens = x.split(' - ')   #self.blacklisted_packages
1844                                 name = tokens[0].strip()
1845                                 if not any(name.endswith(x) for x in self.unwanted_extensions):
1846                                         l = len(tokens)
1847                                         version = l > 1 and tokens[1].strip() or ""
1848                                         self.installed_packetlist[name] = version
1849                 self.buildPacketList()
1850
1851         def buildEntryComponent(self, name, version, description, state):
1852                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_SKIN, "skin_default/div-h.png"))
1853                 if state == 'installed':
1854                         installedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/installed.png"))
1855                         return((name, version, description, state, installedpng, divpng))       
1856                 elif state == 'upgradeable':
1857                         upgradeablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/upgradeable.png"))
1858                         return((name, version, description, state, upgradeablepng, divpng))     
1859                 else:
1860                         installablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_CURRENT_PLUGIN, "SystemPlugins/SoftwareManager/installable.png"))
1861                         return((name, version, description, state, installablepng, divpng))
1862
1863         def buildPacketList(self):
1864                 self.list = []
1865                 self.cachelist = []
1866
1867                 if self.cache_ttl > 0 and self.vc != 0:
1868                         print 'Loading packagelist cache from ',self.cache_file
1869                         try:
1870                                 self.cachelist = load_cache(self.cache_file)
1871                                 if len(self.cachelist) > 0:
1872                                         for x in self.cachelist:
1873                                                 self.list.append(self.buildEntryComponent(x[0], x[1], x[2], x[3]))
1874                                         self['list'].setList(self.list)
1875                         except:
1876                                 self.inv_cache = 1
1877
1878                 if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
1879                         print 'rebuilding fresh package list'
1880                         for x in self.packetlist:
1881                                 status = ""
1882                                 if self.installed_packetlist.has_key(x[0].strip()):
1883                                         if self.installed_packetlist[x[0].strip()] == x[1].strip():
1884                                                 status = "installed"
1885                                                 self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status))
1886                                         else:
1887                                                 status = "upgradeable"
1888                                                 self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status))
1889                                 else:
1890                                         status = "installable"
1891                                         self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), status))
1892                                 if not any(x[0].strip().endswith(x) for x in self.unwanted_extensions):
1893                                         self.cachelist.append([x[0].strip(), x[1].strip(), x[2].strip(), status])
1894                         write_cache(self.cache_file, self.cachelist)
1895                         self['list'].setList(self.list)
1896
1897         def reloadPluginlist(self):
1898                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
1899
1900 class IpkgInstaller(Screen):
1901         skin = """
1902                 <screen name="IpkgInstaller" position="center,center" size="550,450" title="Install extensions" >
1903                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,0" size="140,40" alphatest="on" />
1904                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,0" size="140,40" alphatest="on" />
1905                         <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" />
1906                         <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" />
1907                         <widget name="list" position="5,50" size="540,360" />
1908                         <ePixmap pixmap="skin_default/div-h.png" position="0,410" zPosition="10" size="560,2" transparent="1" alphatest="on" />
1909                         <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" />
1910                 </screen>"""
1911         
1912         def __init__(self, session, list):
1913                 Screen.__init__(self, session)
1914
1915                 self.list = SelectionList()
1916                 self["list"] = self.list
1917                 for listindex in range(len(list)):
1918                         self.list.addSelection(list[listindex], list[listindex], listindex, True)
1919
1920                 self["key_red"] = StaticText(_("Close"))
1921                 self["key_green"] = StaticText(_("Install"))
1922                 self["introduction"] = StaticText(_("Press OK to toggle the selection."))
1923                 
1924                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], 
1925                 {
1926                         "ok": self.list.toggleSelection, 
1927                         "cancel": self.close,
1928                         "red": self.close,
1929                         "green": self.install
1930                 }, -1)
1931
1932         def install(self):
1933                 list = self.list.getSelectionsList()
1934                 cmdList = []
1935                 for item in list:
1936                         cmdList.append((IpkgComponent.CMD_INSTALL, { "package": item[1] }))
1937                 self.session.open(Ipkg, cmdList = cmdList)
1938
1939
1940 def filescan_open(list, session, **kwargs):
1941         filelist = [x.path for x in list]
1942         session.open(IpkgInstaller, filelist) # list
1943
1944 def filescan(**kwargs):
1945         from Components.Scanner import Scanner, ScanPath
1946         return \
1947                 Scanner(mimetypes = ["application/x-debian-package"], 
1948                         paths_to_scan = 
1949                                 [
1950                                         ScanPath(path = "ipk", with_subdirs = True), 
1951                                         ScanPath(path = "", with_subdirs = False), 
1952                                 ], 
1953                         name = "Ipkg", 
1954                         description = _("Install extensions."),
1955                         openfnc = filescan_open, )
1956
1957
1958
1959 def UpgradeMain(session, **kwargs):
1960         session.open(UpdatePluginMenu)
1961
1962 def startSetup(menuid):
1963         if menuid != "setup": 
1964                 return [ ]
1965         return [(_("Software management"), UpgradeMain, "software_manager", 50)]
1966
1967
1968 def Plugins(path, **kwargs):
1969         global plugin_path
1970         plugin_path = path
1971         list = [
1972                 PluginDescriptor(name=_("Software management"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup),
1973                 PluginDescriptor(name=_("Ipkg"), where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)
1974         ]
1975         if config.usage.setup_level.index >= 2: # expert+
1976                 list.append(PluginDescriptor(name=_("Software management"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=UpgradeMain))
1977         return list