add delete to advanced restore
[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         skin = """
654                 <screen position="80,90" size="560,420" title="Plugin manager..." >
655                         <widget source="list" render="Listbox" position="5,10" size="550,365" scrollbarMode="showOnDemand">
656                                 <convert type="TemplatedMultiContent">
657                                 {"templates":
658                                         {"default": [
659                                                         MultiContentEntryText(pos = (30, 1), size = (500, 28), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name
660                                                         MultiContentEntryText(pos = (30, 26), size = (500, 20), font=1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the description
661                                                         MultiContentEntryPixmapAlphaTest(pos = (500, 2), size = (48, 48), png = 5), # index 5 is the status pixmap
662                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 50), size = (550, 2), png = 6), # index 6 is the div pixmap
663                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 10), size = (25, 25), png = 7), # index 7 is the selected pixmap
664                                                 ],
665                                         "category": [
666                                                         MultiContentEntryText(pos = (30, 1), size = (500, 28), font=0, flags = RT_HALIGN_LEFT, text = 0), # index 0 is the name
667                                                         MultiContentEntryText(pos = (30, 26), size = (500, 20), font=1, flags = RT_HALIGN_LEFT, text = 2), # index 2 is the description
668                                                         MultiContentEntryPixmapAlphaTest(pos = (500, 2), size = (48, 48), png = 5), # index 5 is the status pixmap
669                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 50), size = (550, 2), png = 6), # index 6 is the div pixmap
670                                                         MultiContentEntryPixmapAlphaTest(pos = (0, 10), size = (25, 25), png = 7), # index 7 is the selected pixmap
671                                                 ]
672                                         },
673                                         "fonts": [gFont("Regular", 22),gFont("Regular", 14)],
674                                         "itemHeight": 52
675                                 }
676                                 </convert>
677                         </widget>
678
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                 lang = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country"
700                 if lang == "en":
701                         self.language = None
702                 else:
703                         self.language = lang
704                 DreamInfoHandler.__init__(self, self.statusCallback, blocking = False, neededTag = 'ALL_TAGS', neededFlag = self.ImageVersion, language = self.language)
705                 self.directory = resolveFilename(SCOPE_METADIR)
706
707                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
708                 {
709                         "ok": self.go,
710                         "back": self.exit,
711                         "red": self.exit,
712                         "green": self.installPlugins,
713                         "yellow": self.changeSelectionState,
714                         "blue": self.go,
715                 }, -1)
716
717                 self.list = []
718                 self.statuslist = []
719                 self.selectedFiles = []
720                 self.categoryList = []
721                 self["list"] = List(self.list)
722                 self["closetext"] = Label(_("Close"))
723                 self["installtext"] = Label()
724                 self["selecttext"] = Label()
725                 self["viewtext"] = Label()
726
727                 self.list_updating = True
728                 self.packetlist = []
729                 self.installed_packetlist = {}
730                 self.Console = Console()
731                 self.cmdList = []
732                 self.oktext = _("\nAfter pressing OK, please wait!")
733                 self.unwanted_extensions = ('-dbg', '-dev', '-doc')
734
735                 self.ipkg = IpkgComponent()
736                 self.ipkg.addCallback(self.ipkgCallback)
737                 if not self.selectionChanged in self["list"].onSelectionChanged:
738                         self["list"].onSelectionChanged.append(self.selectionChanged)
739
740                 self["installtext"].hide()
741                 self["selecttext"].hide()
742                 self["viewtext"].hide()
743                 self.currList = ""
744
745                 self.onShown.append(self.setWindowTitle)
746                 self.onLayoutFinish.append(self.rebuildList)
747
748         def setWindowTitle(self):
749                 self.setTitle(_("Plugin manager"))
750
751         def exit(self):
752                 if self.currList == "packages":
753                         self.currList = "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
820         def changeSelectionState(self):
821                 current = self["list"].getCurrent()
822                 if current:
823                         if current[8] is not '':
824                                 idx = self["list"].getIndex()
825                                 count = 0
826                                 newList = []
827                                 for x in self.list:
828                                         detailsFile = x[1]
829                                         if idx == count:
830                                                 if x[8] == True:
831                                                         SelectState = False
832                                                         for entry in self.selectedFiles:
833                                                                 if entry[0] == detailsFile:
834                                                                         self.selectedFiles.remove(entry)
835                                                 else:
836                                                         SelectState = True
837                                                         alreadyinList = False
838                                                         for entry in self.selectedFiles:
839                                                                 if entry[0] == detailsFile:
840                                                                         alreadyinList = True
841                                                         if not alreadyinList:
842                                                                 self.selectedFiles.append((detailsFile,x[4],x[3]))
843                                                 newList.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), x[3].strip(), x[4].strip(), selected = SelectState))
844                                         else:
845                                                 newList.append(x)
846                                         count += 1
847
848                                 self.list = newList
849                                 self['list'].updateList(self.list)
850                                 self.selectionChanged()
851
852         def rebuildList(self):
853                 self.setState('update')
854                 self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
855
856         def ipkgCallback(self, event, param):
857                 if event == IpkgComponent.EVENT_ERROR:
858                         self.list_updating = False
859                         self.setState('error')
860                 elif event == IpkgComponent.EVENT_DONE:
861                         if self.list_updating:
862                                 self.list_updating = False
863                                 if not self.Console:
864                                         self.Console = Console()
865                                 cmd = "ipkg list" ###  ipkg install enigma2-plugins-meta
866                                 self.Console.ePopen(cmd, self.IpkgList_Finished)
867                 pass
868
869         def IpkgList_Finished(self, result, retval, extra_args = None):
870                 if len(result):
871                         self.fillPackagesIndexList()
872                 if not self.Console:
873                         self.Console = Console()
874                 self.setState('sync')
875                 cmd = "ipkg list_installed"
876                 self.Console.ePopen(cmd, self.IpkgListInstalled_Finished)
877
878         def IpkgListInstalled_Finished(self, result, retval, extra_args = None):
879                 if len(result):
880                         self.installed_packetlist = {}
881                         for x in result.splitlines():
882                                 split = x.split(' - ')
883                                 if not any(split[0].strip().endswith(x) for x in self.unwanted_extensions):
884                                         self.installed_packetlist[split[0].strip()] = split[1].strip()
885                 self.buildCategoryList()
886
887         def go(self, returnValue = None):
888                 current = self["list"].getCurrent()
889                 if current:
890                         if self.currList == "category":
891                                 selectedTag = current[4]
892                                 self.buildPacketList(selectedTag)
893                         elif self.currList == "packages":
894                                 if current[8] is not '':
895                                         detailsfile = self.directory[0] + "/" + current[1]
896                                         if (os_path.exists(detailsfile) == True):
897                                                 self.session.openWithCallback(self.detailsClosed, PluginDetails, self.skin_path, current)
898                                         else:
899                                                 self.session.open(MessageBox, _("Sorry, no Details available!"), MessageBox.TYPE_INFO)
900         def detailsClosed(self, result):
901                 if result:
902                         if not self.Console:
903                                 self.Console = Console()
904                         self.setState('sync')
905                         cmd = "ipkg list_installed"
906                         self.Console.ePopen(cmd, self.IpkgListInstalled_Finished)
907
908         def buildEntryComponent(self, name, details, description, packagename, state, selected = False):
909                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
910                 if selected is False:
911                         selectedicon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/lock_off.png"))
912                 else:
913                         selectedicon = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/icons/lock_on.png"))
914
915                 if state == 'installed':
916                         installedpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installed.png"))
917                         return((name, details, description, packagename, state, installedpng, divpng, selectedicon, selected))
918                 else:
919                         installablepng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installable.png"))
920                         return((name, details, description, packagename, state, installablepng, divpng, selectedicon, selected))
921
922         def buildPacketList(self, categorytag = None):
923                 if categorytag is not None:
924                         self.currList = "packages"
925                         #print self.packagesIndexlist
926                         self.packetlist = []
927                         for package in self.packagesIndexlist[:]:
928                                 #print "package--->",package
929                                 prerequisites = package[0]["prerequisites"]
930                                 #print "prerequisite",prerequisites
931                                 if prerequisites.has_key("tag"):
932                                         for foundtag in prerequisites["tag"]:
933                                                 if categorytag == foundtag:
934                                                         attributes = package[0]["attributes"]
935                                                         #print "attributes---->",attributes
936                                                         self.packetlist.append([attributes["name"], attributes["details"], attributes["shortdescription"], attributes["packagename"]])
937
938                         self.list = []
939                         for x in self.packetlist:
940                                 print x
941                                 status = ""
942                                 if self.installed_packetlist.has_key(x[3].strip()):
943                                         status = "installed"
944                                         self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), x[3].strip(), status, selected = False))
945                                 else:
946                                         status = "installable"
947                                         self.list.append(self.buildEntryComponent(x[0].strip(), x[1].strip(), x[2].strip(), x[3].strip(), status, selected = False))
948
949                         if len(self.list):
950                                 self.list.sort(key=lambda x: x[0])
951                         self["list"].style = "default"
952                         self['list'].setList(self.list)
953                         self.selectionChanged()
954
955
956         def buildCategoryList(self):
957                 self.currList = "category"
958                 #print self.packagesIndexlist
959                 self.categories = []
960                 self.categoryList = []
961                 for package in self.packagesIndexlist[:]:
962                         #print "package--->",package
963                         prerequisites = package[0]["prerequisites"]
964                         #print "prerequisite",prerequisites
965                         if prerequisites.has_key("tag"):
966                                 for foundtag in prerequisites["tag"]:
967                                         #print "found tag----",foundtag
968                                         if foundtag not in self.categories:
969                                                 self.categories.append(foundtag)
970                                                 self.categoryList.append(self.buildCategoryComponent(foundtag))
971                 self.categoryList.sort(key=lambda x: x[0])
972                 self["list"].style = "category"
973                 self['list'].setList(self.categoryList)
974                 self.selectionChanged()
975
976         def buildCategoryComponent(self, tag = None):
977                 divpng = LoadPixmap(cached=True, path=resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
978                 if tag is not None:
979                         if tag == 'System':
980                                 return(( _("System"), '', _("View list of available system extensions" ),'', tag, None, divpng, None, '' ))
981                         elif tag == 'Skin':
982                                 return(( _("Skins"), '', _("View list of available skins" ),'', tag, None, divpng, None, '' ))
983                         elif tag == 'Recording':
984                                 return(( _("Recordings"), '', _("View list of available recording extensions" ),'', tag, None, divpng, None, '' ))
985                         elif tag == 'Network':
986                                 return(( _("Network"), '', _("View list of available networking extensions" ),'', tag, None, divpng, None, '' ))
987                         elif tag == 'CI':
988                                 return(( _("CommonInterface"), '', _("View list of available CommonInterface extensions" ),'', tag, None, divpng, None, '' ))
989                         elif tag == 'Default':
990                                 return(( _("Default Settings"), '', _("View list of available default settings" ),'', tag, None, divpng, None, '' ))
991                         elif tag == 'SAT':
992                                 return(( _("Satteliteequipment"), '', _("View list of available Satteliteequipment extensions." ),'', tag, None, divpng, None, '' ))
993                         elif tag == 'Software':
994                                 return(( _("Software"), '', _("View list of available software extensions" ),'', tag, None, divpng, None, '' ))
995                         elif tag == 'Multimedia':
996                                 return(( _("Multimedia"), '', _("View list of available multimedia extensions." ),'', tag, None, divpng, None, '' ))
997                         elif tag == 'Display':
998                                 return(( _("Display and Userinterface"), '', _("View list of available Display and Userinterface extensions." ),'', tag, None, divpng, None, '' ))
999                         elif tag == 'EPG':
1000                                 return(( _("Electronic Program Guide"), '', _("View list of available EPG extensions." ),'', tag, None, divpng, None, '' ))
1001                         elif tag == 'Communication':
1002                                 return(( _("Communication"), '', _("View list of available communication extensions." ),'', tag, None, divpng, None, '' ))
1003
1004
1005         def installPlugins(self):
1006                 self.cmdList = []
1007                 if self.selectedFiles and len(self.selectedFiles):
1008                         for plugin in self.selectedFiles:
1009                                 #print "processing Plugin-->",plugin
1010                                 detailsfile = self.directory[0] + "/" + plugin[0]
1011                                 if (os_path.exists(detailsfile) == True):
1012                                         self.fillPackageDetails(plugin[0])
1013                                         self.package = self.packageDetails[0]
1014                                         if self.package[0].has_key("attributes"):
1015                                                 self.attributes = self.package[0]["attributes"]
1016                                         if self.attributes.has_key("package"):
1017                                                 self.packagefiles = self.attributes["package"]
1018                                         if plugin[1] == 'installed':
1019                                                 if self.packagefiles:
1020                                                         for package in self.packagefiles[:]:
1021                                                                 #print "removing package: ",package["name"]
1022                                                                 self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": package["name"] }))
1023                                         else:
1024                                                 if self.packagefiles:
1025                                                         for package in self.packagefiles[:]:
1026                                                                 #print "adding package: ",package["name"]
1027                                                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package["name"] }))
1028                                 else:
1029                                         if plugin[1] == 'installed':
1030                                                 #print "removing package: ",plugin[2]
1031                                                 self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": plugin[2] }))
1032                                         else:
1033                                                 #print "adding package: ",plugin[2]
1034                                                 self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": plugin[2] }))
1035                 else:
1036                         current = self["list"].getCurrent()
1037                         if current:
1038                                 if current[8] is not '':
1039                                         detailsfile = self.directory[0] + "/" + current[1]
1040                                         if (os_path.exists(detailsfile) == True):
1041                                                 self.fillPackageDetails(current[1])
1042                                                 self.package = self.packageDetails[0]
1043                                                 if self.package[0].has_key("attributes"):
1044                                                         self.attributes = self.package[0]["attributes"]
1045                                                 if self.attributes.has_key("package"):
1046                                                         self.packagefiles = self.attributes["package"]
1047                                                 if current[4] == 'installed':
1048                                                         if self.packagefiles:
1049                                                                 for package in self.packagefiles[:]:
1050                                                                         #print "removing package: ",package["name"]
1051                                                                         self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": package["name"] }))
1052                                                 else:
1053                                                         if self.packagefiles:
1054                                                                 for package in self.packagefiles[:]:
1055                                                                         #print "adding package: ",package["name"]
1056                                                                         self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package["name"] }))
1057                                         else:
1058                                                 if current[4] == 'installed':
1059                                                         #print "removing package: ",current[0]
1060                                                         self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": current[3] }))
1061                                                 else:
1062                                                         #print "adding package: ",current[0]
1063                                                         self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": current[3] }))
1064
1065                 if len(self.cmdList):
1066                         print self.cmdList
1067                         self.session.openWithCallback(self.runExecute, MessageBox, _("Do you want to continue installing or removing selected plugins?\n") + self.oktext)
1068
1069         def runExecute(self, result):
1070                 if result:
1071                         self.session.openWithCallback(self.runExecuteFinished, Ipkg, cmdList = self.cmdList)
1072
1073         def runExecuteFinished(self):
1074                 self.session.openWithCallback(self.ExecuteReboot, MessageBox, _("Install or remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1075
1076         def ExecuteReboot(self, result):
1077                 if result is None:
1078                         return
1079                 if result is False:
1080                         self.reloadPluginlist()
1081                         self.detailsClosed(True)
1082                 if result:
1083                         quitMainloop(3)
1084
1085         def reloadPluginlist(self):
1086                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
1087
1088
1089 class PluginDetails(Screen, DreamInfoHandler):
1090         skin = """
1091                 <screen name="PluginDetails" position="60,90" size="600,420" title="PluginDetails..." >
1092                         <widget name="author" position="10,10" size="500,25" zPosition="10" font="Regular;21" transparent="1" />
1093                         <widget name="statuspic" position="550,0" size="48,48" alphatest="on"/>
1094                         <widget name="divpic" position="0,40" size="600,2" alphatest="on"/>
1095                         <widget name="detailtext" position="10,50" size="270,330" zPosition="10" font="Regular;21" transparent="1" halign="left" valign="top"/>
1096                         <widget name="screenshot" position="290,50" size="300,330" alphatest="on"/>
1097                         <ePixmap pixmap="skin_default/buttons/red.png" position="0,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
1098                         <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" />
1099                         <ePixmap pixmap="skin_default/buttons/green.png" position="140,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
1100                         <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" />
1101                 </screen>"""
1102         def __init__(self, session, plugin_path, packagedata = None):
1103                 Screen.__init__(self, session)
1104                 self.skin_path = plugin_path
1105                 lang = language.getLanguage()[:2] # getLanguage returns e.g. "fi_FI" for "language_country"
1106                 if lang == "en":
1107                         self.language = None
1108                 else:
1109                         self.language = lang
1110                 DreamInfoHandler.__init__(self, self.statusCallback, blocking = False, language = self.language)
1111                 self.directory = resolveFilename(SCOPE_METADIR)
1112                 if packagedata:
1113                         self.pluginname = packagedata[0]
1114                         self.details = packagedata[1]
1115                         self.pluginstate = packagedata[4]
1116                         self.statuspicinstance = packagedata[5]
1117                         self.divpicinstance = packagedata[6]
1118                         self.fillPackageDetails(self.details)
1119
1120                 self.thumbnail = ""
1121
1122                 self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
1123                 {
1124                         "back": self.exit,
1125                         "red": self.exit,
1126                         "green": self.go,
1127                         "up": self.pageUp,
1128                         "down": self.pageDown,
1129                         "left": self.pageUp,
1130                         "right": self.pageDown,
1131                 }, -1)
1132
1133                 self["statuspic"] = Pixmap()
1134                 self["divpic"] = Pixmap()
1135                 self["screenshot"] = Pixmap()
1136                 self["closetext"] = Label(_("Close"))
1137                 self["statetext"] = Label()
1138                 self["detailtext"] = ScrollLabel()
1139                 self["author"] = Label()
1140                 self["statuspic"].hide()
1141                 self["screenshot"].hide()
1142                 self["divpic"].hide()
1143
1144                 self.package = self.packageDetails[0]
1145                 #print "PACKAGE-------im DETAILS",self.package
1146                 if self.package[0].has_key("attributes"):
1147                         self.attributes = self.package[0]["attributes"]
1148
1149                 self.cmdList = []
1150                 self.oktext = _("\nAfter pressing OK, please wait!")
1151                 self.picload = ePicLoad()
1152                 self.picload.PictureData.get().append(self.paintScreenshotPixmapCB)
1153                 self.onShown.append(self.setWindowTitle)
1154                 self.onLayoutFinish.append(self.setInfos)
1155
1156         def setWindowTitle(self):
1157                 self.setTitle(_("Package details for: " + self.pluginname))
1158
1159         def exit(self):
1160                 self.close(False)
1161
1162         def pageUp(self):
1163                 self["detailtext"].pageUp()
1164
1165         def pageDown(self):
1166                 self["detailtext"].pageDown()
1167
1168         def statusCallback(self, status, progress):
1169                 pass
1170
1171         def setInfos(self):
1172                 if self.attributes.has_key("name"):
1173                         self.pluginname = self.attributes["name"]
1174                 else:
1175                         self.pluginname = _("unknown")
1176                 if self.attributes.has_key("author"):
1177                         self.author = self.attributes["author"]
1178                 else:
1179                         self.author = _("unknown")
1180                 if self.attributes.has_key("description"):
1181                         self.description = self.attributes["description"]
1182                 else:
1183                         self.author = _("No description available.")
1184                 self.loadThumbnail(self.attributes)
1185                 self["author"].setText(_("Author: ") + self.author)
1186                 self["detailtext"].setText(self.description.strip())
1187                 if self.pluginstate == 'installable':
1188                         self["statetext"].setText(_("Install"))
1189                 else:
1190                         self["statetext"].setText(_("Remove"))
1191
1192         def loadThumbnail(self, entry):
1193                 thumbnailUrl = None
1194                 if entry.has_key("screenshot"):
1195                         thumbnailUrl = entry["screenshot"]
1196                 if thumbnailUrl is not None:
1197                         self.thumbnail = "/tmp/" + thumbnailUrl.split('/')[-1]
1198                         print "[PluginDetails] downloading screenshot " + thumbnailUrl + " to " + self.thumbnail
1199                         client.downloadPage(thumbnailUrl,self.thumbnail).addCallback(self.setThumbnail).addErrback(self.fetchFailed)
1200                 else:
1201                         self.setThumbnail(noScreenshot = True)
1202
1203         def setThumbnail(self, noScreenshot = False):
1204                 if not noScreenshot:
1205                         filename = self.thumbnail
1206                 else:
1207                         filename = resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/noprev.png")
1208
1209                 sc = AVSwitch().getFramebufferScale()
1210                 self.picload.setPara((self["screenshot"].instance.size().width(), self["screenshot"].instance.size().height(), sc[0], sc[1], False, 1, "#00000000"))
1211                 self.picload.startDecode(filename)
1212
1213                 if self.statuspicinstance != None:
1214                         self["statuspic"].instance.setPixmap(self.statuspicinstance.__deref__())
1215                         self["statuspic"].show()
1216                 if self.divpicinstance != None:
1217                         self["divpic"].instance.setPixmap(self.divpicinstance.__deref__())
1218                         self["divpic"].show()
1219
1220         def paintScreenshotPixmapCB(self, picInfo=None):
1221                 ptr = self.picload.getData()
1222                 if ptr != None:
1223                         self["screenshot"].instance.setPixmap(ptr.__deref__())
1224                         self["screenshot"].show()
1225                 else:
1226                         self.setThumbnail(noScreenshot = True)
1227
1228         def go(self):
1229                 if self.attributes.has_key("package"):
1230                         self.packagefiles = self.attributes["package"]
1231                 self.cmdList = []
1232                 if self.pluginstate == 'installed':
1233                         if self.packagefiles:
1234                                 for package in self.packagefiles[:]:
1235                                         #print "removing packagefile: ",package["name"]
1236                                         self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": package["name"] }))
1237                                         if len(self.cmdList):
1238                                                 self.session.openWithCallback(self.runRemove, MessageBox, _("Do you want to remove the package:\n") + self.pluginname + "\n" + self.oktext)
1239                 else:
1240                         if self.packagefiles:
1241                                 for package in self.packagefiles[:]:
1242                                         #print "adding packagefile: ",package["name"]
1243                                         self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": package["name"] }))
1244                                         if len(self.cmdList):
1245                                                 self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n") + self.pluginname + "\n" + self.oktext)
1246
1247         def runUpgrade(self, result):
1248                 if result:
1249                         self.session.openWithCallback(self.runUpgradeFinished, Ipkg, cmdList = self.cmdList)
1250
1251         def runUpgradeFinished(self):
1252                 self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Installation finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1253
1254         def UpgradeReboot(self, result):
1255                 if result is None:
1256                         return
1257                 if result is False:
1258                         self.close(True)
1259                 if result:
1260                         quitMainloop(3)
1261
1262         def runRemove(self, result):
1263                 if result:
1264                         self.session.openWithCallback(self.runRemoveFinished, Ipkg, cmdList = self.cmdList)
1265
1266         def runRemoveFinished(self):
1267                 self.session.openWithCallback(self.RemoveReboot, MessageBox, _("Remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
1268
1269         def RemoveReboot(self, result):
1270                 if result is None:
1271                         return
1272                 if result is False:
1273                         self.close(True)
1274                 if result:
1275                         quitMainloop(3)
1276
1277         def reloadPluginlist(self):
1278                 plugins.readPluginList(resolveFilename(SCOPE_PLUGINS))
1279
1280         def fetchFailed(self,string):
1281                 self.setThumbnail(noScreenshot = True)
1282                 print "[PluginDetails] fetch failed " + string.getErrorMessage()
1283
1284
1285 class UpdatePlugin(Screen):
1286         skin = """
1287                 <screen position="100,100" size="550,200" title="Software Update..." >
1288                         <widget name="activityslider" position="0,0" size="550,5"  />
1289                         <widget name="slider" position="0,100" size="550,30"  />
1290                         <widget name="package" position="10,30" size="540,20" font="Regular;18"/>
1291                         <widget name="status" position="10,60" size="540,45" font="Regular;18"/>
1292                 </screen>"""
1293                 
1294         def __init__(self, session, args = None):
1295                 self.skin = UpdatePlugin.skin
1296                 Screen.__init__(self, session)
1297                 
1298                 self.sliderPackages = { "dreambox-dvb-modules": 1, "enigma2": 2, "tuxbox-image-info": 3 }
1299                 
1300                 self.slider = Slider(0, 4)
1301                 self["slider"] = self.slider
1302                 self.activityslider = Slider(0, 100)
1303                 self["activityslider"] = self.activityslider
1304                 self.status = Label(_("Upgrading Dreambox... Please wait"))
1305                 self["status"] = self.status
1306                 self.package = Label()
1307                 self["package"] = self.package
1308                 
1309                 self.packages = 0
1310                 self.error = 0
1311                 
1312                 self.activity = 0
1313                 self.activityTimer = eTimer()
1314                 self.activityTimer.callback.append(self.doActivityTimer)
1315                 self.activityTimer.start(100, False)
1316                                 
1317                 self.ipkg = IpkgComponent()
1318                 self.ipkg.addCallback(self.ipkgCallback)
1319                 
1320                 self.updating = True
1321                 self.package.setText(_("Package list update"))
1322                 self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
1323                         
1324                 self["actions"] = ActionMap(["WizardActions"], 
1325                 {
1326                         "ok": self.exit,
1327                         "back": self.exit
1328                 }, -1)
1329                 
1330         def doActivityTimer(self):
1331                 self.activity += 1
1332                 if self.activity == 100:
1333                         self.activity = 0
1334                 self.activityslider.setValue(self.activity)
1335                 
1336         def ipkgCallback(self, event, param):
1337                 if event == IpkgComponent.EVENT_DOWNLOAD:
1338                         self.status.setText(_("Downloading"))
1339                 elif event == IpkgComponent.EVENT_UPGRADE:
1340                         if self.sliderPackages.has_key(param):
1341                                 self.slider.setValue(self.sliderPackages[param])
1342                         self.package.setText(param)
1343                         self.status.setText(_("Upgrading"))
1344                         self.packages += 1
1345                 elif event == IpkgComponent.EVENT_INSTALL:
1346                         self.package.setText(param)
1347                         self.status.setText(_("Installing"))
1348                         self.packages += 1
1349                 elif event == IpkgComponent.EVENT_CONFIGURING:
1350                         self.package.setText(param)
1351                         self.status.setText(_("Configuring"))
1352                 elif event == IpkgComponent.EVENT_MODIFIED:
1353                         self.session.openWithCallback(
1354                                 self.modificationCallback,
1355                                 MessageBox,
1356                                 _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param)
1357                         )
1358                 elif event == IpkgComponent.EVENT_ERROR:
1359                         self.error += 1
1360                 elif event == IpkgComponent.EVENT_DONE:
1361                         if self.updating:
1362                                 self.updating = False
1363                                 self.ipkg.startCmd(IpkgComponent.CMD_UPGRADE, args = {'test_only': False})
1364                         elif self.error == 0:
1365                                 self.slider.setValue(4)
1366                                 
1367                                 self.activityTimer.stop()
1368                                 self.activityslider.setValue(0)
1369                                 
1370                                 self.package.setText("")
1371                                 self.status.setText(_("Done - Installed or upgraded %d packages") % self.packages)
1372                         else:
1373                                 self.activityTimer.stop()
1374                                 self.activityslider.setValue(0)
1375                                 error = _("your dreambox might be unusable now. Please consult the manual for further assistance before rebooting your dreambox.")
1376                                 if self.packages == 0:
1377                                         error = _("No packages were upgraded yet. So you can check your network and try again.")
1378                                 if self.updating:
1379                                         error = _("Your dreambox isn't connected to the internet properly. Please check it and try again.")
1380                                 self.status.setText(_("Error") +  " - " + error)
1381                 #print event, "-", param
1382                 pass
1383
1384         def modificationCallback(self, res):
1385                 self.ipkg.write(res and "N" or "Y")
1386
1387         def exit(self):
1388                 if not self.ipkg.isRunning():
1389                         if self.packages != 0 and self.error == 0:
1390                                 self.session.openWithCallback(self.exitAnswer, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"))
1391                         else:
1392                                 self.close()
1393                         
1394         def exitAnswer(self, result):
1395                 if result is not None and result:
1396                         quitMainloop(2)
1397                 self.close()
1398
1399
1400 class IpkgInstaller(Screen):
1401         skin = """
1402                 <screen position="100,100" size="550,400" title="..." >
1403                         <widget name="red" halign="center" valign="center" position="0,0" size="140,60" backgroundColor="red" font="Regular;21" />
1404                         <widget name="green" halign="center" valign="center" position="140,0" text="Install selected" size="140,60" backgroundColor="green" font="Regular;21" />
1405                         <widget name="yellow" halign="center" valign="center" position="280,0" size="140,60" backgroundColor="yellow" font="Regular;21" />
1406                         <widget name="blue" halign="center" valign="center" position="420,0" size="140,60" backgroundColor="blue" font="Regular;21" />
1407                         <widget name="list" position="0,60" size="550,360" />
1408                 </screen>
1409                 """
1410         
1411         def __init__(self, session, list):
1412                 self.skin = IpkgInstaller.skin
1413                 Screen.__init__(self, session)
1414
1415                 self.list = SelectionList()
1416                 self["list"] = self.list
1417                 for listindex in range(len(list)):
1418                         self.list.addSelection(list[listindex], list[listindex], listindex, True)
1419
1420                 self["red"] = Label()
1421                 self["green"] = Label()
1422                 self["yellow"] = Label()
1423                 self["blue"] = Label()
1424                 
1425                 self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], 
1426                 {
1427                         "ok": self.list.toggleSelection, 
1428                         "cancel": self.close, 
1429                         "green": self.install
1430                 }, -1)
1431                 
1432         def install(self):
1433                 list = self.list.getSelectionsList()
1434                 cmdList = []
1435                 for item in list:
1436                         cmdList.append((IpkgComponent.CMD_INSTALL, { "package": item[1] }))
1437                 self.session.open(Ipkg, cmdList = cmdList)
1438
1439 def filescan_open(list, session, **kwargs):
1440         filelist = [x.path for x in list]
1441         session.open(IpkgInstaller, filelist) # list
1442
1443 def filescan(**kwargs):
1444         from Components.Scanner import Scanner, ScanPath
1445         return \
1446                 Scanner(mimetypes = ["application/x-debian-package"], 
1447                         paths_to_scan = 
1448                                 [
1449                                         ScanPath(path = "ipk", with_subdirs = True), 
1450                                         ScanPath(path = "", with_subdirs = False), 
1451                                 ], 
1452                         name = "Ipkg", 
1453                         description = _("Install software updates..."),
1454                         openfnc = filescan_open, )
1455
1456
1457
1458 def UpgradeMain(session, **kwargs):
1459         session.open(UpdatePluginMenu)
1460
1461 def startSetup(menuid):
1462         if menuid != "setup": 
1463                 return [ ]
1464         return [(_("Software manager"), UpgradeMain, "software_manager", 50)]
1465
1466 def Plugins(path, **kwargs):
1467         global plugin_path
1468         plugin_path = path
1469         list = [
1470                 PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup), 
1471                 PluginDescriptor(name=_("Ipkg"), where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)
1472         ]
1473         if config.usage.setup_level.index >= 2: # expert+       
1474                 list.append(PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_EXTENSIONSMENU, fnc=UpgradeMain))     
1475         return list