# QuickInstallerTool plone 3.1, 3.2, 3.3
    security.declareProtected(ManagePortal, 'listInstallableProducts')
    def listInstallableProducts(self, skipInstalled=True):
        """List candidate CMF products for installation -> list of dicts
           with keys:(id,title,hasError,status)
        """
        # reset the list of broken products
        self.errors = {}

        # Get product list from control panel
        pids = self.Control_Panel.Products.objectIds()
        pids = [p for p in pids if self.isProductInstallable(p)]

        # Get product list from the extension profiles
        profile_pids = self.listInstallableProfiles()
        profile_pids = [p for p in profile_pids if self.isProductInstallable(p)]
        for p in profile_pids:
            if p.startswith('Products.'):
                p = p[9:]
            if p not in pids:
                pids.append(p)

        if skipInstalled:
            installed=[p['id'] for p in self.listInstalledProducts(showHidden=True)]
            pids=[r for r in pids if r not in installed]

        res=[]
        for r in pids:
            p=self._getOb(r,None)
            name = r
            profile = self.getInstallProfile(r)
            if profile:
                name = profile['title']
            if p:
                res.append({'id':r, 'title':name, 'status':p.getStatus(),
                            'hasError':p.hasError()})
            else:
                res.append({'id':r, 'title':name,'status':'new', 'hasError':False})
        res.sort(lambda x,y: cmp(x.get('title', x.get('id', None)),
                                 y.get('title', y.get('id', None))))
        return res

    security.declareProtected(ManagePortal, 'listInstalledProducts')
    def listInstalledProducts(self, showHidden=False):
        """Returns a list of products that are installed -> list of
        dicts with keys:(id, title, hasError, status, isLocked, isHidden,
        installedVersion)
        """
        pids = [o.id for o in self.objectValues()
                if o.isInstalled() and (o.isVisible() or showHidden)]
        pids = [pid for pid in pids if self.isProductInstallable(pid)]

        res=[]

        for r in pids:
            p = self._getOb(r,None)
            name = r
            profile = self.getInstallProfile(r)
            if profile:
                name = profile['title']
            
            res.append({'id':r,
                        'title':name,
                        'status':p.getStatus(),
                        'hasError':p.hasError(),
                        'isLocked':p.isLocked(),
                        'isHidden':p.isHidden(),
                        'installedVersion':p.getInstalledVersion()})
        res.sort(lambda x,y: cmp(x.get('title', x.get('id', None)),
                                 y.get('title', y.get('id', None))))
        return res

# QuickInstallerTool plone 4.0.5 -> 4.1.2
    def listInstallableProducts(self, skipInstalled=True):
        """List candidate CMF products for installation -> list of dicts
           with keys:(id,title,hasError,status)
        """
        # reset the list of broken products
        self.errors = {}

        # Returns full names with Products. prefix for all packages / products
        packages = get_packages()

        pids = []
        for p in packages:
            if not self.isProductInstallable(p):
                continue
            if p.startswith('Products.'):
                p = p[9:]
            pids.append(p)

        # Get product list from the extension profiles
        profile_pids = self.listInstallableProfiles()

        for p in profile_pids:
            if p in pids or p in packages:
                continue
            if not self.isProductInstallable(p):
                continue
            pids.append(p)

        if skipInstalled:
            installed=[p['id'] for p in self.listInstalledProducts(showHidden=True)]
            pids=[r for r in pids if r not in installed]

        res=[]
        for r in pids:
            p=self._getOb(r,None)
            name = r
            profile = self.getInstallProfile(r)
            if profile:
                name = profile['title']
            if p:
                res.append({'id':r, 'title':name, 'status':p.getStatus(),
                            'hasError':p.hasError()})
            else:
                res.append({'id':r, 'title':name,'status':'new', 'hasError':False})
        res.sort(lambda x,y: cmp(x.get('title', x.get('id', None)),
                                 y.get('title', y.get('id', None))))
        return res

    def listInstalledProducts(self, showHidden=False):
        """Returns a list of products that are installed -> list of
        dicts with keys:(id, title, hasError, status, isLocked, isHidden,
        installedVersion)
        """
        pids = [o.id for o in self.objectValues()
                if o.isInstalled() and (o.isVisible() or showHidden)]
        pids = [pid for pid in pids if self.isProductInstallable(pid)]

        res=[]

        for r in pids:
            p = self._getOb(r,None)
            name = r
            profile = self.getInstallProfile(r)
            if profile:
                name = profile['title']

            res.append({'id':r,
                        'title':name,
                        'status':p.getStatus(),
                        'hasError':p.hasError(),
                        'isLocked':p.isLocked(),
                        'isHidden':p.isHidden(),
                        'installedVersion':p.getInstalledVersion()})
        res.sort(lambda x,y: cmp(x.get('title', x.get('id', None)),
                                 y.get('title', y.get('id', None))))
        return res
