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