add SoftwareManager and show it inside Setup Menu
authoracid-burn <acidburn@opendreambox.org>
Fri, 13 Feb 2009 10:47:46 +0000 (11:47 +0100)
committeracid-burn <acidburn@opendreambox.org>
Fri, 13 Feb 2009 10:47:46 +0000 (11:47 +0100)
16 files changed:
lib/python/Plugins/SystemPlugins/SoftwareManager/.cvsignore [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/SoftwareManager/BackupRestore.py [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/ImageWizard.py [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/LICENSE [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/Makefile.am [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/__init__.py [new file with mode: 0644]
lib/python/Plugins/SystemPlugins/SoftwareManager/blue.png [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/green.png [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/imagewizard.xml [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/installable.png [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/installed.png [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/plugin.py [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/red.png [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/update.png [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/upgradeable.png [new file with mode: 0755]
lib/python/Plugins/SystemPlugins/SoftwareManager/yellow.png [new file with mode: 0755]

diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/.cvsignore b/lib/python/Plugins/SystemPlugins/SoftwareManager/.cvsignore
new file mode 100644 (file)
index 0000000..138b9cc
--- /dev/null
@@ -0,0 +1,4 @@
+*.pyc
+*.pyo
+Makefile
+Makefile.in
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/BackupRestore.py b/lib/python/Plugins/SystemPlugins/SoftwareManager/BackupRestore.py
new file mode 100755 (executable)
index 0000000..f92472d
--- /dev/null
@@ -0,0 +1,313 @@
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Screens.Console import Console
+from Components.ActionMap import ActionMap, NumberActionMap
+from Components.Pixmap import Pixmap
+from Components.Label import Label
+from Components.MenuList import MenuList
+from Components.config import getConfigListEntry, configfile, ConfigSelection, ConfigSubsection, ConfigText, ConfigLocations
+from Components.config import config
+from Components.ConfigList import ConfigList,ConfigListScreen
+from Components.FileList import MultiFileSelectList
+from Plugins.Plugin import PluginDescriptor
+from enigma import eTimer
+from Tools.Directories import *
+from os import popen, path, makedirs, listdir, access, stat, rename, remove, W_OK, R_OK
+from time import gmtime, strftime, localtime
+from datetime import date
+
+
+config.plugins.configurationbackup = ConfigSubsection()
+config.plugins.configurationbackup.backuplocation = ConfigText(default = '/media/hdd/', visible_width = 50, fixed_size = False)
+config.plugins.configurationbackup.backupdirs = ConfigLocations(default=['/etc/enigma2/', '/etc/network/interfaces', '/etc/wpa_supplicant.conf'])
+
+def getBackupPath():
+       backuppath = config.plugins.configurationbackup.backuplocation.value
+       if backuppath.endswith('/'):
+               return backuppath + 'backup'
+       else:
+               return backuppath + '/backup'
+
+def getBackupFilename():
+       return "enigma2settingsbackup.tar.gz"
+               
+
+class BackupScreen(Screen, ConfigListScreen):
+       skin = """
+               <screen position="135,144" size="350,310" title="Backup running..." >
+               <widget name="config" position="10,10" size="330,250" transparent="1" scrollbarMode="showOnDemand" />
+               </screen>"""
+               
+       def __init__(self, session, runBackup = False):
+               Screen.__init__(self, session)
+               self.session = session
+               self.runBackup = runBackup
+               self["actions"] = ActionMap(["WizardActions", "DirectionActions"], 
+               {
+                       "ok": self.close,
+                       "back": self.close,
+                       "cancel": self.close,
+               }, -1)
+               self.finished_cb = None
+               self.backuppath = getBackupPath()
+               self.backupfile = getBackupFilename()
+               self.fullbackupfilename = self.backuppath + "/" + self.backupfile
+               self.list = []
+               ConfigListScreen.__init__(self, self.list)
+               self.onLayoutFinish.append(self.layoutFinished)
+               if self.runBackup:
+                       self.onShown.append(self.doBackup)
+
+       def layoutFinished(self):
+               self.setWindowTitle()
+
+       def setWindowTitle(self):
+               self.setTitle(_("Backup running..."))
+
+       def doBackup(self):
+               try:
+                       if (path.exists(self.backuppath) == False):
+                               makedirs(self.backuppath)
+                       self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value )
+                       if path.exists(self.fullbackupfilename):
+                               dt = str(date.fromtimestamp(stat(self.fullbackupfilename).st_ctime))
+                               self.newfilename = self.backuppath + "/" + dt + '-' + self.backupfile
+                               if path.exists(self.newfilename):
+                                       remove(self.newfilename)
+                               rename(self.fullbackupfilename,self.newfilename)
+                       if self.finished_cb:
+                               self.session.openWithCallback(self.finished_cb, Console, title = _("Backup running"), cmdlist = ["tar -czvf " + self.fullbackupfilename + " " + self.backupdirs],finishedCallback = self.backupFinishedCB,closeOnSuccess = True)
+                       else:
+                               self.session.open(Console, title = _("Backup running"), cmdlist = ["tar -czvf " + self.fullbackupfilename + " " + self.backupdirs],finishedCallback = self.backupFinishedCB, closeOnSuccess = True)
+               except OSError:
+                       if self.finished_cb:
+                               self.session.openWithCallback(self.finished_cb, MessageBox, _("Sorry your backup destination is not writeable.\nPlease choose an other one."), MessageBox.TYPE_INFO)
+                       else:
+                               self.session.openWithCallback(self.backupErrorCB,MessageBox, _("Sorry your backup destination is not writeable.\nPlease choose an other one."), MessageBox.TYPE_INFO)
+
+       def backupFinishedCB(self,retval = None):
+               self.close(True)
+
+       def backupErrorCB(self,retval = None):
+               self.close(False)
+
+       def runAsync(self, finished_cb):
+               self.finished_cb = finished_cb
+               self.doBackup()
+               
+
+class BackupSelection(Screen):
+       skin = """
+       <screen position="135,125" size="450,310" title="Select files/folders to backup...">
+               <widget name="checkList" position="10,10" size="430,250" transparent="1" scrollbarMode="showOnDemand" />
+               <ePixmap position="0,265" zPosition="1" size="135,40" pixmap="skin_default/buttons/red.png" transparent="1" alphatest="on" />
+               <widget name="key_red" position="0,265" zPosition="2" size="135,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />       
+               <ePixmap position="135,265" zPosition="1" size="135,40" pixmap="skin_default/buttons/green.png" transparent="1" alphatest="on" />
+               <widget name="key_green" position="135,265" zPosition="2" size="135,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
+               <ePixmap position="270,265" zPosition="1" size="135,40" pixmap="skin_default/buttons/yellow.png" transparent="1" alphatest="on" />
+               <widget name="key_yellow" position="270,265" zPosition="2" size="135,40" halign="center" valign="center" font="Regular;22" transparent="1" shadowColor="black" shadowOffset="-1,-1" />
+       </screen>"""
+
+       def __init__(self, session):
+               Screen.__init__(self, session)
+               self.skin_path = plugin_path
+               self["key_red"] = Label(_("Cancel"))
+               self["key_green"] = Label(_("Save"))
+               self["key_yellow"] = Label()
+               
+               self.selectedFiles = config.plugins.configurationbackup.backupdirs.value
+               defaultDir = '/'
+               inhibitDirs = ["/bin", "/boot", "/dev", "/autofs", "/lib", "/proc", "/sbin", "/sys", "/hdd", "/tmp", "/mnt", "/media"]
+               self.filelist = MultiFileSelectList(self.selectedFiles, defaultDir, inhibitDirs = inhibitDirs )
+               self["checkList"] = self.filelist
+               
+               self["actions"] = ActionMap(["DirectionActions", "OkCancelActions", "ShortcutActions"],
+               {
+                       "cancel": self.exit,
+                       "red": self.exit,
+                       "yellow": self.changeSelectionState,
+                       "green": self.saveSelection,
+                       "ok": self.okClicked,
+                       "left": self.left,
+                       "right": self.right,
+                       "down": self.down,
+                       "up": self.up
+               }, -1)
+               if not self.selectionChanged in self["checkList"].onSelectionChanged:
+                       self["checkList"].onSelectionChanged.append(self.selectionChanged)
+               self.onLayoutFinish.append(self.layoutFinished)
+
+       def layoutFinished(self):
+               idx = 0
+               self["checkList"].moveToIndex(idx)
+               self.setWindowTitle()
+               self.selectionChanged()
+
+       def setWindowTitle(self):
+               self.setTitle(_("Select files/folders to backup..."))
+
+       def selectionChanged(self):
+               current = self["checkList"].getCurrent()[0]
+               if current[2] is True:
+                       self["key_yellow"].setText(_("Deselect"))
+               else:
+                       self["key_yellow"].setText(_("Select"))
+               
+       def up(self):
+               self["checkList"].up()
+
+       def down(self):
+               self["checkList"].down()
+
+       def left(self):
+               self["checkList"].pageUp()
+
+       def right(self):
+               self["checkList"].pageDown()
+
+       def changeSelectionState(self):
+               self["checkList"].changeSelectionState()
+               self.selectedFiles = self["checkList"].getSelectedList()
+
+       def saveSelection(self):
+               self.selectedFiles = self["checkList"].getSelectedList()
+               config.plugins.configurationbackup.backupdirs.value = self.selectedFiles
+               config.plugins.configurationbackup.backupdirs.save()
+               config.plugins.configurationbackup.save()
+               config.save()
+               self.close(None)
+
+       def exit(self):
+               self.close(None)
+
+       def okClicked(self):
+               if self.filelist.canDescent():
+                       self.filelist.descent()
+
+
+class RestoreMenu(Screen):
+       skin = """
+               <screen position="135,144" size="450,300" title="Restore backups..." >
+               <widget name="filelist" position="10,10" size="430,240" scrollbarMode="showOnDemand" />
+               <widget name="cancel" position="120,255" size="100,40" pixmap="~/red.png" transparent="1" alphatest="on" />             
+               <widget name="canceltext" position="0,0" size="0,0" valign="center" halign="center" zPosition="2" font="Regular;20" transparent="1" foregroundColor="black" />
+               <widget name="restore" position="230,255" size="100,40" pixmap="~/yellow.png" transparent="1" alphatest="on" />
+               <widget name="restoretext" position="0,0" size="0,0" valign="center" halign="center" zPosition="2" font="Regular;20" transparent="1"  foregroundColor="black" />
+               </screen>"""
+
+       def __init__(self, session, plugin_path):
+               Screen.__init__(self, session)
+               self.skin_path = plugin_path
+               
+               self["canceltext"] = Label(_("Cancel"))
+               self["restoretext"] = Label(_("Restore"))
+               self["restore"] = Pixmap()
+               self["cancel"] = Pixmap()
+
+               self.sel = []
+               self.val = []
+               self.entry = False
+               self.exe = False
+               
+               self.path = ""
+
+               self["actions"] = NumberActionMap(["SetupActions"],
+               {
+                       "ok": self.KeyOk,
+                       "cancel": self.keyCancel
+               }, -1)
+
+               self["shortcuts"] = ActionMap(["ShortcutActions"],
+               {
+                       "red": self.keyCancel,
+                       "yellow": self.KeyOk,
+               })
+               self.flist = []
+               self["filelist"] = MenuList(self.flist)
+               self.fill_list()
+               self.onLayoutFinish.append(self.layoutFinished)
+
+       def layoutFinished(self):
+               self.setWindowTitle()
+
+       def setWindowTitle(self):
+               self.setTitle(_("Restore backups..."))
+
+
+       def fill_list(self):
+               self.flist = []
+               self.path = getBackupPath()
+               if (path.exists(self.path) == False):
+                       makedirs(self.path)
+               for file in listdir(self.path):
+                       if (file.endswith(".tar.gz")):
+                               self.flist.append((file))
+                               self.entry = True
+                               self["filelist"].l.setList(self.flist)
+
+       def KeyOk(self):
+               if (self.exe == False) and (self.entry == True):
+                       self.sel = self["filelist"].getCurrent()
+                       self.val = self.path + self.sel
+                       self.session.openWithCallback(self.startRestore, MessageBox, _("Are you sure you want to restore\nfollowing backup:\n" + self.sel + "\nSystem will restart after the restore!"))
+
+       def keyCancel(self):
+               self.close()
+
+       def startRestore(self, ret = False):
+               if (ret == True):
+                       self.exe = True
+                       self.session.open(Console, title = _("Restore running"), cmdlist = ["tar -xzvf " + self.path + "/" + self.sel + " -C /", "killall -9 enigma2"])
+
+       def Exit(self):
+               self.close()
+
+class RestoreScreen(Screen, ConfigListScreen):
+       skin = """
+               <screen position="135,144" size="350,310" title="Restore running..." >
+               <widget name="config" position="10,10" size="330,250" transparent="1" scrollbarMode="showOnDemand" />
+               </screen>"""
+               
+       def __init__(self, session, runRestore = False):
+               Screen.__init__(self, session)
+               self.session = session
+               self.runRestore = runRestore
+               self["actions"] = ActionMap(["WizardActions", "DirectionActions"], 
+               {
+                       "ok": self.close,
+                       "back": self.close,
+                       "cancel": self.close,
+               }, -1)
+               self.finished_cb = None
+               self.backuppath = getBackupPath()
+               self.backupfile = getBackupFilename()
+               self.fullbackupfilename = self.backuppath + "/" + self.backupfile
+               self.list = []
+               ConfigListScreen.__init__(self, self.list)
+               self.onLayoutFinish.append(self.layoutFinished)
+               if self.runRestore:
+                       self.onShown.append(self.doRestore)
+
+       def layoutFinished(self):
+               self.setWindowTitle()
+
+       def setWindowTitle(self):
+               self.setTitle(_("Restore running..."))
+
+       def doRestore(self):
+               if self.finished_cb:
+                       self.session.openWithCallback(self.finished_cb, Console, title = _("Restore running"), cmdlist = ["tar -xzvf " + self.fullbackupfilename + " -C /", "killall -9 enigma2"])
+               else:
+                       self.session.open(Console, title = _("Restore running"), cmdlist = ["tar -xzvf " + self.fullbackupfilename + " -C /", "killall -9 enigma2"])
+
+       def backupFinishedCB(self,retval = None):
+               self.close(True)
+
+       def backupErrorCB(self,retval = None):
+               self.close(False)
+
+       def runAsync(self, finished_cb):
+               self.finished_cb = finished_cb
+               self.doRestore()
+               
+       
\ No newline at end of file
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/ImageWizard.py b/lib/python/Plugins/SystemPlugins/SoftwareManager/ImageWizard.py
new file mode 100755 (executable)
index 0000000..1797e4f
--- /dev/null
@@ -0,0 +1,120 @@
+from Screens.Wizard import WizardSummary
+from Screens.WizardLanguage import WizardLanguage
+from Screens.Wizard import wizardManager
+from Screens.Rc import Rc
+from Components.Label import Label
+from Components.MenuList import MenuList
+from Components.PluginComponent import plugins
+from Plugins.Plugin import PluginDescriptor
+from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
+from Components.Pixmap import Pixmap, MovingPixmap, MultiPixmap
+from os import popen, path, makedirs, listdir, access, stat, rename, remove, W_OK, R_OK
+
+from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigText, ConfigLocations, ConfigBoolean
+from Components.Harddisk import harddiskmanager
+config.misc.firstrun = ConfigBoolean(default = True)
+config.plugins.configurationbackup = ConfigSubsection()
+config.plugins.configurationbackup.backuplocation = ConfigText(default = '/media/hdd/', visible_width = 50, fixed_size = False)
+config.plugins.configurationbackup.backupdirs = ConfigLocations(default=['/etc/enigma2/', '/etc/network/interfaces', '/etc/wpa_supplicant.conf'])
+
+
+backupfile = "enigma2settingsbackup.tar.gz"
+
+def checkConfigBackup():
+       parts = [ (r.description, r.mountpoint) for r in harddiskmanager.getMountedPartitions(onlyhotplug = False)]
+       for x in parts:
+               if x[1] == '/':
+                       parts.remove(x)
+       if len(parts):
+               for x in parts:
+                       if x[1].endswith('/'):
+                               fullbackupfile =  x[1] + 'backup/' + backupfile
+                               if fileExists(fullbackupfile):
+                                       config.plugins.configurationbackup.backuplocation.value = str(x[1])
+                                       config.plugins.configurationbackup.backuplocation.save()
+                                       config.plugins.configurationbackup.save()
+                                       return x
+                       else:
+                               fullbackupfile =  x[1] + '/backup/' + backupfile
+                               if fileExists(fullbackupfile):
+                                       config.plugins.configurationbackup.backuplocation.value = str(x[1])
+                                       config.plugins.configurationbackup.backuplocation.save()
+                                       config.plugins.configurationbackup.save()
+                                       return x
+               return None             
+
+def checkBackupFile():
+       backuplocation = config.plugins.configurationbackup.backuplocation.value
+       if backuplocation.endswith('/'):
+               fullbackupfile =  backuplocation + 'backup/' + backupfile
+               if fileExists(fullbackupfile):
+                       return True
+               else:
+                       return False
+       else:
+               fullbackupfile =  backuplocation + '/backup/' + backupfile
+               if fileExists(fullbackupfile):
+                       return True
+               else:
+                       return False
+
+if checkConfigBackup() is None:
+       backupAvailable = 0
+else:
+       backupAvailable = 1
+
+class ImageWizard(WizardLanguage, Rc):
+       skin = """
+               <screen name="ImageWizard" position="0,0" size="720,576" title="Welcome..." flags="wfNoBorder" >
+                       <widget name="text" position="153,40" size="340,330" font="Regular;22" />
+                       <widget source="list" render="Listbox" position="43,340" size="490,180" scrollbarMode="showOnDemand" >
+                               <convert type="StringList" />
+                       </widget>
+                       <widget name="config" position="53,340" zPosition="1" size="440,180" transparent="1" scrollbarMode="showOnDemand" />
+                       <ePixmap pixmap="skin_default/buttons/button_red.png" position="40,225" zPosition="0" size="15,16" transparent="1" alphatest="on" />
+                       <widget name="languagetext" position="55,225" size="95,30" font="Regular;18" />
+                       <widget name="wizard" pixmap="skin_default/wizard.png" position="40,50" zPosition="10" size="110,174" alphatest="on" />
+                       <widget name="rc" pixmaps="skin_default/rc.png,skin_default/rcold.png" position="530,50" zPosition="10" size="154,500" alphatest="on" />
+                       <widget name="arrowdown" pixmap="skin_default/arrowdown.png" position="-100,-100" zPosition="11" size="37,70" alphatest="on" />
+                       <widget name="arrowdown2" pixmap="skin_default/arrowdown.png" position="-100,-100" zPosition="11" size="37,70" alphatest="on" />
+                       <widget name="arrowup" pixmap="skin_default/arrowup.png" position="-100,-100" zPosition="11" size="37,70" alphatest="on" />
+                       <widget name="arrowup2" pixmap="skin_default/arrowup.png" position="-100,-100" zPosition="11" size="37,70" alphatest="on" />
+               </screen>"""
+       def __init__(self, session):
+               self.xmlfile = resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/imagewizard.xml")
+               WizardLanguage.__init__(self, session, showSteps = False, showStepSlider = False)
+               Rc.__init__(self)
+               self.session = session
+               self["wizard"] = Pixmap()
+               self.selectedDevice = None
+               
+       def markDone(self):
+               pass
+
+       def listDevices(self):
+               list = [ (r.description, r.mountpoint) for r in harddiskmanager.getMountedPartitions(onlyhotplug = False)]
+               for x in list:
+                       result = access(x[1], W_OK) and access(x[1], R_OK)
+                       if result is False or x[1] == '/':
+                               list.remove(x)
+               for x in list:
+                       if x[1].startswith('/autofs/'):
+                               list.remove(x)  
+               return list
+
+       def deviceSelectionMade(self, index):
+               self.deviceSelect(index)
+               
+       def deviceSelectionMoved(self):
+               self.deviceSelect(self.selection)
+               
+       def deviceSelect(self, device):
+               self.selectedDevice = device
+               config.plugins.configurationbackup.backuplocation.value = self.selectedDevice
+               config.plugins.configurationbackup.backuplocation.save()
+               config.plugins.configurationbackup.save()
+
+       
+if config.misc.firstrun.value:
+       wizardManager.registerWizard(ImageWizard, backupAvailable, priority = 10)
+
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/LICENSE b/lib/python/Plugins/SystemPlugins/SoftwareManager/LICENSE
new file mode 100755 (executable)
index 0000000..835b9dc
--- /dev/null
@@ -0,0 +1,17 @@
+This plugin is licensed under the Creative Commons 
+Attribution-NonCommercial-ShareAlike 3.0 Unported 
+License. To view a copy of this license, visit
+http://creativecommons.org/licenses/by-nc-sa/3.0/ or send a letter to Creative
+Commons, 559 Nathan Abbott Way, Stanford, California 94305, USA.
+
+Alternatively, this plugin may be distributed and executed on hardware which
+is licensed by Dream Multimedia GmbH.
+
+This plugin is NOT free software. It is open source, you are allowed to
+modify it (if you keep the license), but it may not be commercially 
+distributed other than under the conditions noted above.
+
+Some Icons used are taken from NX10 icons by Mazenl77
+(http://www.iconspedia.com/pack/nx10-1-6/)
+licensed under Creative Commons Attribution 3.0 Unported
+http://creativecommons.org/licenses/by/3.0/
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/Makefile.am b/lib/python/Plugins/SystemPlugins/SoftwareManager/Makefile.am
new file mode 100755 (executable)
index 0000000..f94498f
--- /dev/null
@@ -0,0 +1,12 @@
+installdir = $(LIBDIR)/enigma2/python/Plugins/SystemPlugins/SoftwareManager
+
+install_PYTHON =       \
+       __init__.py \
+       plugin.py \
+       BackupRestore.py \
+       ImageWizard.py \
+       imagewizard.xml \
+       *.png 
+
+install_DATA = LICENSE
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/__init__.py b/lib/python/Plugins/SystemPlugins/SoftwareManager/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/blue.png b/lib/python/Plugins/SystemPlugins/SoftwareManager/blue.png
new file mode 100755 (executable)
index 0000000..a392bbe
Binary files /dev/null and b/lib/python/Plugins/SystemPlugins/SoftwareManager/blue.png differ
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/green.png b/lib/python/Plugins/SystemPlugins/SoftwareManager/green.png
new file mode 100755 (executable)
index 0000000..f168e4d
Binary files /dev/null and b/lib/python/Plugins/SystemPlugins/SoftwareManager/green.png differ
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/imagewizard.xml b/lib/python/Plugins/SystemPlugins/SoftwareManager/imagewizard.xml
new file mode 100755 (executable)
index 0000000..98658b7
--- /dev/null
@@ -0,0 +1,100 @@
+<wizard>\r
+       <step id="restorequestion">\r
+               <condition>\r
+from Plugins.SystemPlugins.SoftwareManager.ImageWizard import checkConfigBackup\r
+self.backuppath = checkConfigBackup()\r
+self.condition = (self.backuppath is not None and config.misc.firstrun.value)\r
+               </condition>\r
+               <text value="Do you want to restore your settings?" />\r
+               <list>\r
+                       <listentry caption="Yes, restore the settings now" step="restore" />\r
+                       <listentry caption="No, just start my dreambox" step="end" />\r
+               </list>\r
+               <code>\r
+self.clearSelectedKeys()\r
+self.selectKey("OK")\r
+               </code>\r
+       </step>\r
+\r
+       <step id="welcome">\r
+               <text value="Welcome to the Image upgrade wizard. The wizard will assist you in upgrading the firmware of your Dreambox by providing a backup facility for your current settings and a short explanation of how to upgrade your firmware." />\r
+               <list>\r
+                       <listentry caption="OK, guide me through the upgrade process" step="backupquestion" />\r
+                       <listentry caption="Exit the wizard" step="end" />\r
+               </list>\r
+       </step>\r
+\r
+       <step id="backupquestion">\r
+               <text value="The wizard can backup your current settings. Do you want to do a backup now?" />\r
+               <list>\r
+                       <listentry caption="Yes, backup my settings!" step="backupwhere" />\r
+                       <listentry caption="No backup needed" step="upgrade" />\r
+               </list>\r
+       </step>\r
+\r
+       <step id="backupwhere" nextstep="backup">\r
+               <text value="Where do you want to backup your settings?" />\r
+               <list type="dynamic" source="listDevices" evaluation="deviceSelectionMade" onselect="deviceSelectionMoved" />\r
+       </step>\r
+\r
+       <step id="backup" nextstep="backupresult">\r
+               <text value="You have chosen to backup your settings. Please press OK to start the backup now." />\r
+               <config screen="BackupScreen" module="Plugins.SystemPlugins.SoftwareManager.BackupRestore" type="ConfigList" />\r
+               <code pos="after" async="yes">\r
+self.currStep = self.getStepWithID('backupresult')\r
+self.afterAsyncCode()\r
+               </code> \r
+       </step>\r
+               \r
+       <step id="backupresult" nextstep="upgrade">\r
+               <condition>\r
+from Plugins.SystemPlugins.SoftwareManager.ImageWizard import checkBackupFile\r
+self.backuppath = checkBackupFile()\r
+self.condition = (self.backuppath is True)\r
+               </condition>\r
+               <text value="Your backup succeeded. We will now continue to explain the further upgrade process." />\r
+       </step>\r
+\r
+       <step id="backupresult" nextstep="backupwhere">\r
+               <condition>\r
+from Plugins.SystemPlugins.SoftwareManager.ImageWizard import checkBackupFile\r
+self.backuppath = checkBackupFile()\r
+self.condition = (self.backuppath is False)\r
+               </condition>\r
+               <text value="The backup failed. Please choose a different backup location." />\r
+       </step>\r
+\r
+       <step id="restore" nextstep="backupresult">\r
+               <text value="You have chosen to restore your settings. Enigma2 will restart after restore. Please press OK to start the restore now." />\r
+               <config screen="RestoreScreen" module="Plugins.SystemPlugins.SoftwareManager.BackupRestore" type="ConfigList" />\r
+       </step>\r
+               \r
+\r
+       <step id="upgradehow">\r
+               <text value="The wizard can backup your current settings. Do you want to do a backup now?" />\r
+               <list>\r
+                       <listentry caption="Install a new image with your web browser" step="upgrade" />\r
+                       <listentry caption="Install a new image with a USB stick" step="upgradeUSB" />\r
+               </list>\r
+       </step>\r
+               \r
+       <step id="upgrade">\r
+               <text value="You need a PC connected to your dreambox. If you need further instructions, please visit the website http://www.dm7025.de.\nYour dreambox will now be halted. After you have performed the update instructions from the website, your new firmware will ask you to restore your settings." />\r
+               <list>\r
+                       <listentry caption="Yes, perform a shutdown now." step="shutdown" />\r
+                       <listentry caption="No, do nothing." step="end" />\r
+               </list>\r
+       </step>\r
+\r
+       <step id="shutdown" nextstep="shutdown">\r
+               <code>\r
+from enigma import quitMainloop\r
+quitMainloop(1)\r
+               </code>\r
+               <text value="Your dreambox is shutting down. Please stand by..." />\r
+       </step>\r
+               \r
+       <step id="end">\r
+               <text value="The wizard is finished now." />\r
+       </step>\r
+</wizard>\r
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/installable.png b/lib/python/Plugins/SystemPlugins/SoftwareManager/installable.png
new file mode 100755 (executable)
index 0000000..710f420
Binary files /dev/null and b/lib/python/Plugins/SystemPlugins/SoftwareManager/installable.png differ
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/installed.png b/lib/python/Plugins/SystemPlugins/SoftwareManager/installed.png
new file mode 100755 (executable)
index 0000000..d6a1bc1
Binary files /dev/null and b/lib/python/Plugins/SystemPlugins/SoftwareManager/installed.png differ
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/plugin.py b/lib/python/Plugins/SystemPlugins/SoftwareManager/plugin.py
new file mode 100755 (executable)
index 0000000..1835a2e
--- /dev/null
@@ -0,0 +1,728 @@
+from Plugins.Plugin import PluginDescriptor
+from Screens.Console import Console
+from Screens.ChoiceBox import ChoiceBox
+from Screens.MessageBox import MessageBox
+from Screens.Screen import Screen
+from Screens.Ipkg import Ipkg
+from Components.ActionMap import ActionMap, NumberActionMap
+from Components.Input import Input
+from Components.Ipkg import IpkgComponent
+from Components.Label import Label
+from Components.MenuList import MenuList
+from Components.Sources.List import List
+from Components.Slider import Slider
+from Components.Harddisk import harddiskmanager
+from Components.config import config,getConfigListEntry, ConfigSubsection, ConfigText, ConfigLocations
+from Components.Console import Console
+from Components.MultiContent import MultiContentEntryText, MultiContentEntryPixmapAlphaTest
+from Components.SelectionList import SelectionList
+from Tools.Directories import fileExists, resolveFilename, SCOPE_PLUGINS, SCOPE_SKIN_IMAGE
+from Tools.LoadPixmap import LoadPixmap
+from enigma import eTimer,  loadPNG, quitMainloop, RT_HALIGN_LEFT, RT_VALIGN_CENTER, eListboxPythonMultiContent, eListbox, gFont
+from cPickle import dump, load
+
+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
+from time import time, gmtime, strftime, localtime
+from stat import ST_MTIME
+from datetime import date
+
+from ImageWizard import ImageWizard
+from BackupRestore import BackupSelection, RestoreMenu, BackupScreen, RestoreScreen, getBackupPath, getBackupFilename
+
+config.plugins.configurationbackup = ConfigSubsection()
+config.plugins.configurationbackup.backuplocation = ConfigText(default = '/media/hdd/', visible_width = 50, fixed_size = False)
+config.plugins.configurationbackup.backupdirs = ConfigLocations(default=['/etc/enigma2/', '/etc/network/interfaces', '/etc/wpa_supplicant.conf'])
+
+
+def write_cache(cache_file, cache_data):
+       #Does a cPickle dump
+       if not os_path.isdir( os_path.dirname(cache_file) ):
+               try:
+                       mkdir( os_path.dirname(cache_file) )
+               except OSError:
+                           print os_path.dirname(cache_file), 'is a file'
+       fd = open(cache_file, 'w')
+       dump(cache_data, fd, -1)
+       fd.close()
+
+def valid_cache(cache_file, cache_ttl):
+       #See if the cache file exists and is still living
+       try:
+               mtime = stat(cache_file)[ST_MTIME]
+       except:
+               return 0
+       curr_time = time()
+       if (curr_time - mtime) > cache_ttl:
+               return 0
+       else:
+               return 1
+
+def load_cache(cache_file):
+       #Does a cPickle load
+       fd = open(cache_file)
+       cache_data = load(fd)
+       fd.close()
+       return cache_data
+
+
+class UpdatePluginMenu(Screen):
+       skin = """
+               <screen name="UpdatePluginMenu" position="90,130" size="550,330" title="Softwaremanager..." >
+                       <ePixmap pixmap="skin_default/border_menu.png" position="10,10" zPosition="1" size="250,300" transparent="1" alphatest="on" />
+                       <widget source="menu" render="Listbox" position="20,20" size="230,260" scrollbarMode="showOnDemand">
+                               <convert type="TemplatedMultiContent">
+                                       {"template": [
+                                                       MultiContentEntryText(pos = (2, 2), size = (230, 22), flags = RT_HALIGN_LEFT, text = 1), # index 0 is the MenuText,
+                                               ],
+                                       "fonts": [gFont("Regular", 20)],
+                                       "itemHeight": 25
+                                       }
+                               </convert>
+                       </widget>
+                       <widget name="description" position="280,10" size="230,300" font="Regular;19" halign="center" valign="center" />
+               </screen>"""
+               
+       def __init__(self, session, args = 0):
+               Screen.__init__(self, session)
+               self.skin_path = plugin_path
+               self.menu = args
+               self.list = []
+               self.oktext = _("\nPress OK on your remote control to continue.")
+               self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value )
+               if self.menu == 0:
+                       self.list.append(("software-update", _("Software update"), _("\nOnline update of your Dreambox software." ) + self.oktext) )
+                       self.list.append(("software-restore", _("Software restore"), _("\nRestore your Dreambox with a new firmware." ) + self.oktext))
+                       self.list.append(("system-backup", _("Backup system settings"), _("\nBackup your Dreambox settings." ) + self.oktext))
+                       self.list.append(("system-restore",_("Restore system settings"), _("\nRestore your Dreambox settings." ) + self.oktext))
+                       if config.usage.setup_level.index >= 2: # expert+
+                               self.list.append(("advanced", _("Advanced Options"), _("\nAdvanced options and settings." ) + self.oktext))
+               elif self.menu == 1:
+                       self.list.append(("ipkg-manager", _("Packet management"),  _("\nView, install and remove available or installed packages." ) + self.oktext))
+                       self.list.append(("ipkg-install", _("Install local IPKG"),  _("\nScan for local packages and install them." ) + self.oktext))
+                       self.list.append(("advancedrestore", _("Advanced restore"), _("\nRestore your backups by date." ) + self.oktext))
+                       self.list.append(("backuplocation", _("Choose backup location"),  _("\nSelect your backup device.\nCurrent device: " ) + config.plugins.configurationbackup.backuplocation.value + self.oktext ))
+                       self.list.append(("backupfiles", _("Choose backup files"),  _("Select files for backup. Currently selected:\n" ) + self.backupdirs + self.oktext))
+                       self.list.append(("ipkg-source",_("Choose upgrade source"), _("\nEdit the upgrade source address." ) + self.oktext))
+
+               self["menu"] = List(self.list)
+               self["menu"].onSelectionChanged.append(self.selectionChanged)
+               self["description"] = Label()
+                               
+               self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"],
+               {
+                       "ok": self.go,
+                       "back": self.close,
+                       "red": self.close,
+               }, -1)
+
+               self.onLayoutFinish.append(self.layoutFinished)
+               self.backuppath = getBackupPath()
+               self.backupfile = getBackupFilename()
+               self.fullbackupfilename = self.backuppath + "/" + self.backupfile
+               self.onShown.append(self.setWindowTitle)
+               
+       def layoutFinished(self):
+               idx = 0
+               self["menu"].index = idx
+               self.loadDescription()
+               
+       def setWindowTitle(self):
+               self.setTitle(_("Software manager..."))
+               
+       def loadDescription(self):
+               if self["menu"].getCurrent()[0] == 'software-update':
+                       self["description"].setText(self["menu"].getCurrent()[2] )
+               if self["menu"].getCurrent()[0] == 'software-restore':
+                       self["description"].setText(self["menu"].getCurrent()[2] )
+               if self["menu"].getCurrent()[0] == 'ipkg-install':
+                       self["description"].setText(self["menu"].getCurrent()[2] )
+               if self["menu"].getCurrent()[0] == 'system-backup':
+                       self["description"].setText(self["menu"].getCurrent()[2] )
+               if self["menu"].getCurrent()[0] == 'system-restore':
+                       self["description"].setText(self["menu"].getCurrent()[2] )
+               if self["menu"].getCurrent()[0] == 'advanced':
+                       self["description"].setText(self["menu"].getCurrent()[2] )
+               if self["menu"].getCurrent()[0] == 'ipkg-source':
+                       self["description"].setText(self["menu"].getCurrent()[2] )              
+               if self["menu"].getCurrent()[0] == 'ipkg-manager':
+                       self["description"].setText(self["menu"].getCurrent()[2] )
+               if self["menu"].getCurrent()[0] == 'advancedrestore':
+                       self["description"].setText(self["menu"].getCurrent()[2] )
+               if self["menu"].getCurrent()[0] == 'backuplocation':
+                       self["description"].setText(self["menu"].getCurrent()[2] )
+               if self["menu"].getCurrent()[0] == 'backupfiles':
+                       self["description"].setText(self["menu"].getCurrent()[2] )                      
+
+       def go(self):
+               if self.menu == 0:
+                       if (self["menu"].getCurrent()[0] == "software-restore"):
+                               self.session.open(ImageWizard)
+                       if (self["menu"].getCurrent()[0] == "software-update"):
+                               self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to update your Dreambox?")+"\n"+_("\nAfter pressing OK, please wait!"))
+                       if (self["menu"].getCurrent()[0] == "advanced"):
+                               self.session.open(UpdatePluginMenu, 1)
+                       if (self["menu"].getCurrent()[0] == "system-backup"):
+                               self.session.openWithCallback(self.backupDone,BackupScreen, runBackup = True)
+                       if (self["menu"].getCurrent()[0] == "system-restore"):
+                               if os_path.exists(self.fullbackupfilename):
+                                       self.session.openWithCallback(self.startRestore, MessageBox, _("Are you sure you want to restore your Enigma2 backup?\nEnigma2 will restart after the restore"))
+                               else:   
+                                       self.session.open(MessageBox, _("Sorry no backups found!"), MessageBox.TYPE_INFO)
+               if self.menu == 1:
+                       if (self["menu"].getCurrent()[0] == "ipkg-manager"):
+                               self.session.open(PacketManager, self.skin_path)
+                       if (self["menu"].getCurrent()[0] == "ipkg-source"):
+                               self.session.open(IPKGSource)
+                       if (self["menu"].getCurrent()[0] == "ipkg-install"):
+                               try:
+                                       from Plugins.Extensions.MediaScanner.plugin import main
+                                       main(self.session)
+                               except:
+                                       self.session.open(MessageBox, _("Sorry MediaScanner is not installed!"), MessageBox.TYPE_INFO)
+                       if (self["menu"].getCurrent()[0] == "backuplocation"):
+                               parts = [ (r.description, r.mountpoint, self.session) for r in harddiskmanager.getMountedPartitions(onlyhotplug = False)]
+                               for x in parts:
+                                       if not access(x[1], F_OK|R_OK|W_OK) or x[1] == '/':
+                                               parts.remove(x)
+                               for x in parts:
+                                       if x[1].startswith('/autofs/'):
+                                               parts.remove(x)                                 
+                               if len(parts):
+                                       self.session.openWithCallback(self.backuplocation_choosen, ChoiceBox, title = _("Please select medium to use as backup location"), list = parts)
+                       if (self["menu"].getCurrent()[0] == "backupfiles"):
+                               self.session.openWithCallback(self.backupfiles_choosen,BackupSelection)
+                       if (self["menu"].getCurrent()[0] == "advancedrestore"):
+                               self.session.open(RestoreMenu, self.skin_path)                  
+
+       def selectionChanged(self):
+               self.loadDescription()
+
+       def backupfiles_choosen(self, ret):
+               self.backupdirs = ' '.join( config.plugins.configurationbackup.backupdirs.value )
+               self.loadDescription()
+
+       def backuplocation_choosen(self, option):
+               if option is not None:
+                       config.plugins.configurationbackup.backuplocation.value = str(option[1])
+               config.plugins.configurationbackup.backuplocation.save()
+               config.plugins.configurationbackup.save()
+               config.save()
+               self.createBackupfolders()
+               self.loadDescription()
+       
+       def runUpgrade(self, result):
+               if result:
+                       self.session.openWithCallback(self.runFinished,UpdatePlugin, self.skin_path)
+
+       def runFinished(self):
+               self.session.openWithCallback(self.reboot, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
+               
+       def reboot(self, result):
+               if result is None:
+                       return
+               if result:
+                       quitMainloop(3)
+
+       def createBackupfolders(self):
+               print "Creating backup folder if not already there..."
+               self.backuppath = getBackupPath()
+               try:
+                       if (os_path.exists(self.backuppath) == False):
+                               makedirs(self.backuppath)
+               except OSError:
+                       self.session.open(MessageBox, _("Sorry, your backup destination is not writeable.\n\nPlease choose another one."), MessageBox.TYPE_INFO)
+
+       def backupDone(self,retval = None):
+               if retval is True:
+                       self.session.open(MessageBox, _("Backup done."), MessageBox.TYPE_INFO)
+               else:
+                       self.session.open(MessageBox, _("Backup failed."), MessageBox.TYPE_INFO)
+
+       def startRestore(self, ret = False):
+               if (ret == True):
+                       self.exe = True
+                       self.session.open(RestoreScreen, runRestore = True)
+
+
+class IPKGSource(Screen):
+       skin = """
+               <screen position="100,100" size="550,60" title="IPKG source" >
+                       <widget name="text" position="0,0" size="550,25" font="Regular;20" backgroundColor="background" foregroundColor="#cccccc" />
+               </screen>"""
+               
+       def __init__(self, session, args = None):
+               Screen.__init__(self, session)
+               self.session = session
+               
+               fp = file('/etc/ipkg/official-feed.conf', 'r')
+               sources = fp.readlines()
+               fp.close()
+               
+               self["text"] = Input(sources[0], maxSize=False, type=Input.TEXT)
+                               
+               self["actions"] = NumberActionMap(["WizardActions", "InputActions", "TextEntryActions", "KeyboardInputActions"], 
+               {
+                       "ok": self.go,
+                       "back": self.close,
+                       "left": self.keyLeft,
+                       "right": self.keyRight,
+                       "home": self.keyHome,
+                       "end": self.keyEnd,
+                       "deleteForward": self.keyDeleteForward,
+                       "deleteBackward": self.keyDeleteBackward,
+                       "1": self.keyNumberGlobal,
+                       "2": self.keyNumberGlobal,
+                       "3": self.keyNumberGlobal,
+                       "4": self.keyNumberGlobal,
+                       "5": self.keyNumberGlobal,
+                       "6": self.keyNumberGlobal,
+                       "7": self.keyNumberGlobal,
+                       "8": self.keyNumberGlobal,
+                       "9": self.keyNumberGlobal,
+                       "0": self.keyNumberGlobal
+               }, -1)
+               
+       def go(self):
+               fp = file('/etc/ipkg/official-feed.conf', 'w')
+               fp.write(self["text"].getText())
+               fp.close()
+               self.close()
+               
+       def keyLeft(self):
+               self["text"].left()
+       
+       def keyRight(self):
+               self["text"].right()
+       
+       def keyHome(self):
+               self["text"].home()
+       
+       def keyEnd(self):
+               self["text"].end()
+       
+       def keyDeleteForward(self):
+               self["text"].delete()
+       
+       def keyDeleteBackward(self):
+               self["text"].deleteBackward()
+       
+       def keyNumberGlobal(self, number):
+               print "pressed", number
+               self["text"].number(number)
+
+
+class PacketList(MenuList):
+       def __init__(self, list, enableWrapAround=True):
+               MenuList.__init__(self, list, enableWrapAround, eListboxPythonMultiContent)
+               self.l.setFont(0, gFont("Regular", 22))
+               self.l.setFont(1, gFont("Regular", 14))
+               self.l.setItemHeight(52)
+
+class PacketManager(Screen):
+       skin = """
+               <screen position="90,80" size="530,420" title="IPKG upgrade..." >
+                       <widget name="list" position="5,10" size="520,365" zPosition="1" scrollbarMode="showOnDemand" />
+                       <widget name="status" position="30,160" size="530,40" zPosition="4" font="Regular;22" halign="left" transparent="1" />
+                       <ePixmap pixmap="skin_default/buttons/red.png" position="10,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
+                       <widget name="closetext" position="20,390" size="140,21" zPosition="10" font="Regular;21" transparent="1" />
+                       <ePixmap pixmap="skin_default/buttons/green.png" position="160,380" zPosition="2" size="140,40" transparent="1" alphatest="on" />
+                       <widget name="reloadtext" position="170,390" size="300,21" zPosition="10" font="Regular;21" transparent="1" />
+               </screen>"""
+               
+       def __init__(self, session, plugin_path, args = None):
+               Screen.__init__(self, session)
+               self.session = session
+               self.skin_path = plugin_path
+
+               self["shortcuts"] = ActionMap(["ShortcutActions", "WizardActions"], 
+               {
+                       "ok": self.go,
+                       "back": self.close,
+                       "red": self.close,
+                       "green": self.reload,
+               }, -1)
+               
+               self.list = []
+               self["list"] = PacketList(self.list)
+               self.status = Label()
+               self["closetext"] = Label(_("Close"))
+               self["reloadtext"] = Label(_("Reload"))
+               self["status"] = self.status                            
+
+               self.list_updating = True
+               self.packetlist = []
+               self.installed_packetlist = {}
+               self.Console = Console()
+               self.cmdList = []
+               self.cachelist = []
+               self.cache_ttl = 86400  #600 is default, 0 disables, Seconds cache is considered valid (24h should be ok for caching ipkgs)
+               self.cache_file = '/usr/lib/enigma2/python/Plugins/SystemPlugins/SoftwareManager/packetmanager.cache' #Path to cache directory   
+               self.oktext = _("\nAfter pressing OK, please wait!")
+
+               self.ipkg = IpkgComponent()
+               self.ipkg.addCallback(self.ipkgCallback)
+               self.onShown.append(self.setWindowTitle)
+               self.onLayoutFinish.append(self.rebuildList)
+               self.onClose.append(self.cleanup)
+               
+       def cleanup(self):
+               self.ipkg.stop()
+               if self.Console is not None:
+                       del self.Console
+
+       def reload(self):
+               if (os_path.exists(self.cache_file) == True):
+                       remove(self.cache_file)
+                       self.list_updating = True
+                       self.rebuildList()
+                       
+       def setWindowTitle(self):
+               self.setTitle(_("Packet manager"))
+
+       def rebuildList(self):
+               self["list"].instance.hide()
+               self.status.setText(_("Package list update"))
+               self.status.show()
+               self.inv_cache = 0
+               self.vc = valid_cache(self.cache_file, self.cache_ttl)
+               if self.cache_ttl > 0 and self.vc != 0:
+                       try:
+                               self.buildPacketList()
+                       except:
+                               self.inv_cache = 1
+               if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
+                       self.run = 0
+                       self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
+
+       def go(self, returnValue = None):
+               returnValue = self['list'].l.getCurrentSelection()[0]
+               self.cmdList = []
+               if returnValue[3] == 'installed':
+                       self.cmdList.append((IpkgComponent.CMD_REMOVE, { "package": returnValue[0] }))
+                       if len(self.cmdList):
+                               self.session.openWithCallback(self.runRemove, MessageBox, _("Do you want to remove the package:\n" + returnValue[0] + "\n" + self.oktext))
+               elif returnValue[3] == 'upgradeable':
+                       self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": returnValue[0] }))
+                       if len(self.cmdList):
+                               self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to upgrade the package:\n" + returnValue[0] + "\n" + self.oktext))
+               else:
+                       self.cmdList.append((IpkgComponent.CMD_INSTALL, { "package": returnValue[0] }))
+                       if len(self.cmdList):
+                               self.session.openWithCallback(self.runUpgrade, MessageBox, _("Do you want to install the package:\n" + returnValue[0] + "\n" + self.oktext))
+
+       def runRemove(self, result):
+               if result:
+                       self.session.openWithCallback(self.runRemoveFinished, Ipkg, cmdList = self.cmdList)
+
+       def runRemoveFinished(self):
+               self.session.openWithCallback(self.RemoveReboot, MessageBox, _("Remove finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
+
+       def RemoveReboot(self, result):
+               if result is None:
+                       return
+               if result is False:
+                       entry = self['list'].l.getCurrentSelection()[0]
+                       item = self['list'].l.getCurrentSelectionIndex()
+                       self.list[item] = PacketEntryComponent([entry[0], entry[1], entry[2], 'installable'])
+                       self.cachelist[item] = [entry[0], entry[1], entry[2], 'installable']
+                       self['list'].l.setList(self.list)
+                       write_cache(self.cache_file, self.cachelist)
+               if result:
+                       quitMainloop(3)
+
+       def runUpgrade(self, result):
+               if result:
+                       self.session.openWithCallback(self.runUpgradeFinished, Ipkg, cmdList = self.cmdList)
+
+       def runUpgradeFinished(self):
+               self.session.openWithCallback(self.UpgradeReboot, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"), MessageBox.TYPE_YESNO)
+               
+       def UpgradeReboot(self, result):
+               if result is None:
+                       return
+               if result is False:
+                       entry = self['list'].l.getCurrentSelection()[0]
+                       item = self['list'].l.getCurrentSelectionIndex()
+                       self.list[item] = PacketEntryComponent([entry[0], entry[1], entry[2], 'installed'])
+                       self.cachelist[item] = [entry[0], entry[1], entry[2], 'installed']
+                       self['list'].l.setList(self.list)
+                       write_cache(self.cache_file, self.cachelist)
+               if result:
+                       quitMainloop(3)
+
+       def ipkgCallback(self, event, param):
+               if event == IpkgComponent.EVENT_ERROR:
+                       self.list_updating = False
+                       self.status.setText(_("An error occured!"))
+               elif event == IpkgComponent.EVENT_DONE:
+                       if self.list_updating:
+                               self.list_updating = False
+                               if not self.Console:
+                                       self.Console = Console()
+                               cmd = "ipkg list"
+                               self.Console.ePopen(cmd, self.IpkgList_Finished)
+               #print event, "-", param
+               pass
+
+       def IpkgList_Finished(self, result, retval, extra_args = None):
+               if len(result):
+                       self.packetlist = []
+                       for x in result.splitlines():
+                               split = x.split(' - ')
+                               self.packetlist.append([split[0].strip(), split[1].strip(),split[2].strip()])
+               cmd = "ipkg list_installed"
+               self.Console.ePopen(cmd, self.IpkgListInstalled_Finished)
+
+       def IpkgListInstalled_Finished(self, result, retval, extra_args = None):
+               if len(result):
+                       self.installed_packetlist = {}
+                       for x in result.splitlines():
+                               split = x.split(' - ')
+                               self.installed_packetlist[split[0].strip()] = split[1].strip()
+               self.buildPacketList()
+
+       def PacketEntryComponent(self,entry):
+               res = [ entry ]
+               res.append(MultiContentEntryText(pos=(5, 1), size=(440, 28), font=0, text= entry[0]))
+               res.append(MultiContentEntryText(pos=(5, 26), size=(440, 20), font=1, text=entry[2]))
+               res.append(MultiContentEntryPixmapAlphaTest(pos=(445, 2), size=(48, 48), png = entry[4]))
+               res.append(MultiContentEntryPixmapAlphaTest(pos=(5, 50), size=(510, 2), png = entry[5]))
+               
+               return res
+
+
+       def buildPacketList(self):
+               self.list = []
+               self.cachelist = []
+               installedpng = loadPNG(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installed.png"))
+               upgradeablepng = loadPNG(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/upgradeable.png"))
+               installablepng = loadPNG(resolveFilename(SCOPE_PLUGINS, "SystemPlugins/SoftwareManager/installable.png"))
+               divpng = loadPNG(resolveFilename(SCOPE_SKIN_IMAGE, "skin_default/div-h.png"))
+
+               if self.cache_ttl > 0 and self.vc != 0:
+                       print 'Loading packagelist cache from ',self.cache_file
+                       try:
+                               self.cachelist = load_cache(self.cache_file)
+                               if len(self.cachelist) > 0:
+                                       for x in self.cachelist:
+                                               if x[3] == 'installed':
+                                                       self.list.append(self.PacketEntryComponent([x[0], x[1], x[2], x[3],installedpng,divpng]))
+                                               elif x[3] == 'upgradeable':
+                                                       self.list.append(self.PacketEntryComponent([x[0], x[1], x[2], x[3],upgradeablepng,divpng]))
+                                               else:
+                                                       self.list.append(self.PacketEntryComponent([x[0], x[1], x[2], x[3],installablepng,divpng]))
+                                       self['list'].l.setList(self.list)
+                                       self["list"].instance.show()
+                                       self.status.hide()
+                       except:
+                               self.inv_cache = 1
+
+               if self.cache_ttl == 0 or self.inv_cache == 1 or self.vc == 0:
+                       print 'rebuilding fresh package list'
+                       for x in self.packetlist:
+                               status = ""
+                               if self.installed_packetlist.has_key(x[0].strip()):
+                                       if self.installed_packetlist[x[0].strip()] == x[1].strip():
+                                               status = "installed"
+                                               self.list.append(self.PacketEntryComponent([x[0].strip(), x[1].strip(), x[2].strip(), status,installedpng,divpng]))
+                                       else:
+                                               status = "upgradeable"
+                                               self.list.append(self.PacketEntryComponent([x[0].strip(), x[1].strip(), x[2].strip(), status,upgradeablepng,divpng]))
+                               else:
+                                       status = "installable"
+                                       self.list.append(self.PacketEntryComponent([x[0].strip(), x[1].strip(), x[2].strip(), status,installablepng,divpng]))
+                               self.cachelist.append([x[0].strip(), x[1].strip(), x[2].strip(), status])       
+                       write_cache(self.cache_file, self.cachelist)
+                       self['list'].l.setList(self.list)
+                       self["list"].instance.show()
+                       self.status.hide()
+
+
+class UpdatePlugin(Screen):
+       skin = """
+               <screen position="100,100" size="550,200" title="Software Update..." >
+                       <widget name="activityslider" position="0,0" size="550,5"  />
+                       <widget name="slider" position="0,100" size="550,30"  />
+                       <widget name="package" position="10,30" size="540,20" font="Regular;18"/>
+                       <widget name="status" position="10,60" size="540,45" font="Regular;18"/>
+               </screen>"""
+               
+       def __init__(self, session, args = None):
+               self.skin = UpdatePlugin.skin
+               Screen.__init__(self, session)
+               
+               self.sliderPackages = { "dreambox-dvb-modules": 1, "enigma2": 2, "tuxbox-image-info": 3 }
+               
+               self.slider = Slider(0, 4)
+               self["slider"] = self.slider
+               self.activityslider = Slider(0, 100)
+               self["activityslider"] = self.activityslider
+               self.status = Label(_("Upgrading Dreambox... Please wait"))
+               self["status"] = self.status
+               self.package = Label()
+               self["package"] = self.package
+               
+               self.packages = 0
+               self.error = 0
+               
+               self.activity = 0
+               self.activityTimer = eTimer()
+               self.activityTimer.callback.append(self.doActivityTimer)
+               self.activityTimer.start(100, False)
+                               
+               self.ipkg = IpkgComponent()
+               self.ipkg.addCallback(self.ipkgCallback)
+               
+               self.updating = True
+               self.package.setText(_("Package list update"))
+               self.ipkg.startCmd(IpkgComponent.CMD_UPDATE)
+                       
+               self["actions"] = ActionMap(["WizardActions"], 
+               {
+                       "ok": self.exit,
+                       "back": self.exit
+               }, -1)
+               
+       def doActivityTimer(self):
+               self.activity += 1
+               if self.activity == 100:
+                       self.activity = 0
+               self.activityslider.setValue(self.activity)
+               
+       def ipkgCallback(self, event, param):
+               if event == IpkgComponent.EVENT_DOWNLOAD:
+                       self.status.setText(_("Downloading"))
+               elif event == IpkgComponent.EVENT_UPGRADE:
+                       if self.sliderPackages.has_key(param):
+                               self.slider.setValue(self.sliderPackages[param])
+                       self.package.setText(param)
+                       self.status.setText(_("Upgrading"))
+                       self.packages += 1
+               elif event == IpkgComponent.EVENT_INSTALL:
+                       self.package.setText(param)
+                       self.status.setText(_("Installing"))
+                       self.packages += 1
+               elif event == IpkgComponent.EVENT_CONFIGURING:
+                       self.package.setText(param)
+                       self.status.setText(_("Configuring"))
+               elif event == IpkgComponent.EVENT_MODIFIED:
+                       self.session.openWithCallback(
+                               self.modificationCallback,
+                               MessageBox,
+                               _("A configuration file (%s) was modified since Installation.\nDo you want to keep your version?") % (param)
+                       )
+               elif event == IpkgComponent.EVENT_ERROR:
+                       self.error += 1
+               elif event == IpkgComponent.EVENT_DONE:
+                       if self.updating:
+                               self.updating = False
+                               self.ipkg.startCmd(IpkgComponent.CMD_UPGRADE, args = {'test_only': False})
+                       elif self.error == 0:
+                               self.slider.setValue(4)
+                               
+                               self.activityTimer.stop()
+                               self.activityslider.setValue(0)
+                               
+                               self.package.setText("")
+                               self.status.setText(_("Done - Installed or upgraded %d packages") % self.packages)
+                       else:
+                               self.activityTimer.stop()
+                               self.activityslider.setValue(0)
+                               error = _("your dreambox might be unusable now. Please consult the manual for further assistance before rebooting your dreambox.")
+                               if self.packages == 0:
+                                       error = _("No packages were upgraded yet. So you can check your network and try again.")
+                               if self.updating:
+                                       error = _("Your dreambox isn't connected to the internet properly. Please check it and try again.")
+                               self.status.setText(_("Error") +  " - " + error)
+               #print event, "-", param
+               pass
+
+       def modificationCallback(self, res):
+               self.ipkg.write(res and "N" or "Y")
+
+       def exit(self):
+               if not self.ipkg.isRunning():
+                       if self.packages != 0 and self.error == 0:
+                               self.session.openWithCallback(self.exitAnswer, MessageBox, _("Upgrade finished.") +" "+_("Do you want to reboot your Dreambox?"))
+                       else:
+                               self.close()
+                       
+       def exitAnswer(self, result):
+               if result is not None and result:
+                       quitMainloop(2)
+               self.close()
+
+
+
+class IpkgInstaller(Screen):
+       skin = """
+               <screen position="100,100" size="550,400" title="..." >
+                       <widget name="red" halign="center" valign="center" position="0,0" size="140,60" backgroundColor="red" font="Regular;21" />
+                       <widget name="green" halign="center" valign="center" position="140,0" text="Install selected" size="140,60" backgroundColor="green" font="Regular;21" />
+                       <widget name="yellow" halign="center" valign="center" position="280,0" size="140,60" backgroundColor="yellow" font="Regular;21" />
+                       <widget name="blue" halign="center" valign="center" position="420,0" size="140,60" backgroundColor="blue" font="Regular;21" />
+                       <widget name="list" position="0,60" size="550,360" />
+               </screen>
+               """
+       
+       def __init__(self, session, list):
+               self.skin = IpkgInstaller.skin
+               Screen.__init__(self, session)
+
+               self.list = SelectionList()
+               self["list"] = self.list
+               for listindex in range(len(list)):
+                       self.list.addSelection(list[listindex], list[listindex], listindex, True)
+
+               self["red"] = Label()
+               self["green"] = Label()
+               self["yellow"] = Label()
+               self["blue"] = Label()
+               
+               self["actions"] = ActionMap(["OkCancelActions", "ColorActions"], 
+               {
+                       "ok": self.list.toggleSelection, 
+                       "cancel": self.close, 
+                       "green": self.install
+               }, -1)
+               
+       def install(self):
+               list = self.list.getSelectionsList()
+               cmdList = []
+               for item in list:
+                       cmdList.append((IpkgComponent.CMD_INSTALL, { "package": item[1] }))
+               self.session.open(Ipkg, cmdList = cmdList)
+
+def filescan_open(list, session, **kwargs):
+       filelist = [x.path for x in list]
+       session.open(IpkgInstaller, filelist) # list
+
+def filescan(**kwargs):
+       from Components.Scanner import Scanner, ScanPath
+       return \
+               Scanner(mimetypes = ["application/x-debian-package"], 
+                       paths_to_scan = 
+                               [
+                                       ScanPath(path = "ipk", with_subdirs = True), 
+                                       ScanPath(path = "", with_subdirs = False), 
+                               ], 
+                       name = "Ipkg", 
+                       description = "Install software updates...", 
+                       openfnc = filescan_open, )
+
+def UpgradeMain(session, **kwargs):
+       session.open(UpdatePluginMenu)
+
+def startSetup(menuid):
+       if menuid != "setup": 
+               return [ ]
+       return [(_("Software manager") + "...", UpgradeMain, "software_manager", 50)]
+
+def Plugins(path, **kwargs):
+       global plugin_path
+       plugin_path = path
+       return [
+               PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), where = PluginDescriptor.WHERE_MENU, fnc=startSetup), 
+               #PluginDescriptor(name=_("Software manager"), description=_("Manage your receiver's software"), icon="update.png", where = PluginDescriptor.WHERE_PLUGINMENU, fnc=UpgradeMain),
+               PluginDescriptor(name=_("Ipkg"), where = PluginDescriptor.WHERE_FILESCAN, fnc = filescan)
+       ]
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/red.png b/lib/python/Plugins/SystemPlugins/SoftwareManager/red.png
new file mode 100755 (executable)
index 0000000..8096045
Binary files /dev/null and b/lib/python/Plugins/SystemPlugins/SoftwareManager/red.png differ
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/update.png b/lib/python/Plugins/SystemPlugins/SoftwareManager/update.png
new file mode 100755 (executable)
index 0000000..0ece6c7
Binary files /dev/null and b/lib/python/Plugins/SystemPlugins/SoftwareManager/update.png differ
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/upgradeable.png b/lib/python/Plugins/SystemPlugins/SoftwareManager/upgradeable.png
new file mode 100755 (executable)
index 0000000..edbbdee
Binary files /dev/null and b/lib/python/Plugins/SystemPlugins/SoftwareManager/upgradeable.png differ
diff --git a/lib/python/Plugins/SystemPlugins/SoftwareManager/yellow.png b/lib/python/Plugins/SystemPlugins/SoftwareManager/yellow.png
new file mode 100755 (executable)
index 0000000..dacb80e
Binary files /dev/null and b/lib/python/Plugins/SystemPlugins/SoftwareManager/yellow.png differ