def processUpdate(): url = addonInfos["url"] if url.endswith('/'): url = url[0:-1] url = "%s.nvda-addon?%s" % (url, urlencode(paramsDL())) fp = os.path.join(globalVars.appArgs.configPath, "%s.nvda-addon" % sectionName) try: dl = URLopener() dl.retrieve(url, fp) try: curAddons = [] for addon in addonHandler.getAvailableAddons(): curAddons.append(addon) bundle = addonHandler.AddonBundle(fp) prevAddon = None bundleName = bundle.manifest['name'] for addon in curAddons: if not addon.isPendingRemove and bundleName == addon.manifest[ 'name']: prevAddon = addon break if prevAddon: prevAddon.requestRemove() addonHandler.installAddonBundle(bundle) core.restart() except BaseException as e: log.error(e) os.startfile(fp) except BaseException as e: log.error(e) return wx.CallAfter(errorUpdateDialog)
def getBundle(): try: bundle = addonHandler.AddonBundle(self.destPath.decode("mbcs")) except AttributeError: bundle = addonHandler.AddonBundle(self.destPath) except: log.error("Error opening addon bundle from %s" % self.destPath, exc_info=True) # Translators: The message displayed when an error occurs when trying to update an add-on package due to package problems. gui.messageBox( _("Cannot update {name} - missing file or invalid file format" ).format(name=self.addonName), makeAddonWindowTitle(NVDAString("Error")), wx.OK | wx.ICON_ERROR) return None return bundle
def processUpdate(): url = configBE._addonURL + "latest?" + urlencode(paramsDL()) fp = os.path.join(globalVars.appArgs.configPath, "brailleExtender.nvda-addon") try: dl = URLopener() dl.retrieve(url, fp) try: curAddons = [] for addon in addonHandler.getAvailableAddons(): curAddons.append(addon) bundle = addonHandler.AddonBundle(fp) prevAddon = None bundleName = bundle.manifest['name'] for addon in curAddons: if not addon.isPendingRemove and bundleName == addon.manifest[ 'name']: prevAddon = addon break if prevAddon: prevAddon.requestRemove() addonHandler.installAddonBundle(bundle) core.restart() except BaseException as e: log.error(e) os.startfile(fp) except BaseException as e: log.error(e) ui.message( _("Unable to save or download update file. Opening your browser" )) os.startfile(url) return
def installAddon(self, addonPath, closeAfter=False): try: try: bundle=addonHandler.AddonBundle(addonPath) except: log.error("Error opening addon bundle from %s"%addonPath,exc_info=True) # Translators: The message displayed when an error occurs when opening an add-on package for adding. gui.messageBox(_("Failed to open add-on package file at %s - missing file or invalid file format")%addonPath, # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return # Translators: A message asking the user if they really wish to install an addon. if gui.messageBox(_("Are you sure you want to install this add-on? Only install add-ons from trusted sources.\nAddon: {summary} {version}\nAuthor: {author}").format(**bundle.manifest), # Translators: Title for message asking if the user really wishes to install an Addon. _("Add-on Installation"), wx.YES|wx.NO|wx.ICON_WARNING)!=wx.YES: return bundleName=bundle.manifest['name'] prevAddon=None for addon in self.curAddons: if not addon.isPendingRemove and bundleName==addon.manifest['name']: prevAddon=addon break if prevAddon: # Translators: A message asking if the user wishes to update a previously installed add-on with this one. if gui.messageBox(_("A version of this add-on is already installed. Would you like to update it?"), # Translators: A title for the dialog asking if the user wishes to update a previously installed add-on with this one. _("Add-on Installation"), wx.YES|wx.NO|wx.ICON_WARNING)!=wx.YES: return prevAddon.requestRemove() progressDialog = gui.IndeterminateProgressDialog(gui.mainFrame, # Translators: The title of the dialog presented while an Addon is being installed. _("Installing Add-on"), # Translators: The message displayed while an addon is being installed. _("Please wait while the add-on is being installed.")) try: gui.ExecAndPump(addonHandler.installAddonBundle,bundle) except: log.error("Error installing addon bundle from %s"%addonPath,exc_info=True) self.refreshAddonsList() progressDialog.done() del progressDialog # Translators: The message displayed when an error occurs when installing an add-on package. gui.messageBox(_("Failed to install add-on from %s")%addonPath, # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return else: self.refreshAddonsList(activeIndex=-1) progressDialog.done() del progressDialog finally: if closeAfter: # #4460: If we do this immediately, wx seems to drop the WM_QUIT sent if the user chooses to restart. # This seems to have something to do with the wx.ProgressDialog. # The CallLater seems to work around this. wx.CallLater(1, self.Close)
def getAddon(): import tempfile, os addon = opener(updateInfo["downloadUrl"]) content = addon.read() addon.close() if updateInfo["size"] != len(content): gui.messageBox(_("Data load error"), _("Error"), style=wx.OK | wx.ICON_ERROR) return fd, path = tempfile.mkstemp(".nvda-addon", "tcmd_addon_update-") with open(path, "wb") as file: file.write(content) import core bundle = addonHandler.AddonBundle(path) try: if not addonHandler.addonVersionCheck.isAddonCompatible(bundle): gui.messageBox(_( "This version of NVDA is incompatible. To install the add-on, NVDA version {year}.{major} or higher is required. Please update NVDA or download an older version of the add-on here: \n{link}" ).format( year=bundle.minimumNVDAVersion[0], major=bundle.minimumNVDAVersion[1], link= "https://github.com/jawhien/extendedTotalCmd/releases/tag/2.5" ), _("Error"), style=wx.OK | wx.ICON_ERROR) os.close(fd) os.unlink(path) return except: pass gui.ExecAndPump(addonHandler.installAddonBundle, bundle) for addon in addonHandler.getAvailableAddons(): if not addon.isPendingRemove and manifest["name"].lower( ) == addon.manifest["name"].lower(): addon.requestRemove() break os.close(fd) os.unlink(path) if gui.messageBox(_( "Changes were made to add-ons. You must restart NVDA for these changes to take effect. Would you like to restart now?" ), _("Restart NVDA"), style=wx.YES | wx.NO | wx.ICON_WARNING) == wx.YES: core.restart()
def processUpdate(expectedSHA256Sum): url = addonInfos["url"] if url.endswith('/'): url = url[0:-1] url = "%s.nvda-addon?%s" % (url, urlencode(paramsDL())) fp = os.path.join(globalVars.appArgs.configPath, "%s.nvda-addon" % sectionName) try: with urllib.request.urlopen(url) as res: with open(fp, "wb") as out: out.write(res.read()) actualSHA256Sum = SHA256Sum(fp) if expectedSHA256Sum != actualSHA256Sum: log.info("Invalid hash for %s. Actual: %s, expected: %s" % (fp, actualSHA256Sum, expectedSHA256Sum)) msg = _( "Hashes do not match! Actual value is '{actualHash}'. Expected is '{expectedHash}'." ).format(actualHash=actualSHA256Sum, expectedHash=expectedSHA256Sum) wx.CallAfter(errorUpdateDialog, msg) return curAddons = [] for addon in addonHandler.getAvailableAddons(): curAddons.append(addon) bundle = addonHandler.AddonBundle(fp) prevAddon = None bundleName = bundle.manifest['name'] for addon in curAddons: if not addon.isPendingRemove and bundleName == addon.manifest[ "name"]: prevAddon = addon break if prevAddon: prevAddon.requestRemove() addonHandler.installAddonBundle(bundle) core.restart() except BaseException as e: log.error(e) return wx.CallAfter(errorUpdateDialog, e)
def onTimer(self): if self.inTimer is True or self.hasBeenUpdated is True: return self.inTimer = True try: evt = self.updater.queue.get_nowait() except queue.Empty: evt = None if evt is not None: filepath = evt.get("download", None) if filepath is not None: import addonHandler for prev in addonHandler.getAvailableAddons(): if prev.name == updater.ADDON_NAME: prev.requestRemove() bundle = addonHandler.AddonBundle(filepath) addonHandler.installAddonBundle(bundle) logHandler.log.info( "Installed version %s, restart NVDA to make the changes permanent" % (evt["version"])) self.hasBeenUpdated = True self.inTimer = False wx.CallLater(1000, self.onTimer)
def download(): global urlName, urlN, directory, bundle p = urllib.request.Request(urlRepos) r = urllib.request.urlopen(p).read() githubApi = json.loads(r.decode('utf-8')) urlName = githubApi[0]['assets'][0]['browser_download_url'] urlN = str(urlName.split("/")[-1:]).replace("[", "").replace( "\'", "").replace("]", "") directory = os.path.join(globalVars.appArgs.configPath, "updates") if os.path.exists(directory) == False: os.mkdir(directory) file = os.path.join(directory, urlN) req = urllib.request.Request(urlName, headers={'User-Agent': 'Mozilla/5.0'}) response = urllib.request.urlopen(req) fileContents = response.read() response.close() f = open(file, "wb") f.write(fileContents) f.close() bundle = addonHandler.AddonBundle(file) if bundle.manifest["name"] == ourAddon.manifest['name']: AddonFlow.checkCompatibility() AddonFlow.doNothing()
def installAddon(parentWindow, addonPath): """ Installs the addon at path. Any error messages / warnings are presented to the user via a GUI message box. If attempting to install an addon that is pending removal, it will no longer be pending removal. :return True on success or False on failure. """ try: bundle = addonHandler.AddonBundle(addonPath) except: log.error("Error opening addon bundle from %s" % addonPath, exc_info=True) gui.messageBox( # Translators: The message displayed when an error occurs when opening an add-on package for adding. _("Failed to open add-on package file at %s - missing file or invalid file format" ) % addonPath, # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return False # Exit early, can't install an invalid bundle if not addonVersionCheck.hasAddonGotRequiredSupport(bundle): _showAddonRequiresNVDAUpdateDialog(parentWindow, bundle) return False # Exit early, addon does not have required support elif not addonVersionCheck.isAddonTested(bundle): _showAddonTooOldDialog(parentWindow, bundle) return False # Exit early, addon is not up to date with the latest API version. elif wx.YES != _showConfirmAddonInstallDialog(parentWindow, bundle): return False # Exit early, User changed their mind about installation. prevAddon = None for addon in addonHandler.getAvailableAddons(): if not addon.isPendingRemove and bundle.name == addon.manifest['name']: prevAddon = addon break if prevAddon: summary = bundle.manifest["summary"] curVersion = prevAddon.manifest["version"] newVersion = bundle.manifest["version"] # Translators: A title for the dialog asking if the user wishes to update a previously installed # add-on with this one. messageBoxTitle = _("Add-on Installation") # Translators: A message asking if the user wishes to update an add-on with the same version # currently installed according to the version number. overwriteExistingAddonInstallationMessage = _( "You are about to install version {newVersion} of {summary}, which appears to be already installed. " "Would you still like to update?").format(summary=summary, newVersion=newVersion) # Translators: A message asking if the user wishes to update a previously installed add-on with this one. updateAddonInstallationMessage = _( "A version of this add-on is already installed. " "Would you like to update {summary} version {curVersion} to version {newVersion}?" ).format(summary=summary, curVersion=curVersion, newVersion=newVersion) if gui.messageBox( overwriteExistingAddonInstallationMessage if curVersion == newVersion else updateAddonInstallationMessage, messageBoxTitle, wx.YES | wx.NO | wx.ICON_WARNING) != wx.YES: return False prevAddon.requestRemove() from contextlib import contextmanager @contextmanager def doneAndDestroy(window): try: yield window except: # pass on any exceptions raise finally: # but ensure that done and Destroy are called. window.done() window.Destroy() # use a progress dialog so users know that something is happening. progressDialog = gui.IndeterminateProgressDialog( parentWindow, # Translators: The title of the dialog presented while an Addon is being installed. _("Installing Add-on"), # Translators: The message displayed while an addon is being installed. _("Please wait while the add-on is being installed.")) try: # Use context manager to ensure that `done` and `Destroy` are called on the progress dialog afterwards with doneAndDestroy(progressDialog): gui.ExecAndPump(addonHandler.installAddonBundle, bundle) return True except: log.error("Error installing addon bundle from %s" % addonPath, exc_info=True) gui.messageBox( # Translators: The message displayed when an error occurs when installing an add-on package. _("Failed to install add-on from %s") % addonPath, # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return False
def _downloadSuccess(self): self._stopped() try: try: bundle = addonHandler.AddonBundle(self.destPath.decode("mbcs")) except AttributeError: bundle = addonHandler.AddonBundle(self.destPath) except: log.error("Error opening addon bundle from %s" % self.destPath, exc_info=True) # Translators: The message displayed when an error occurs when trying to update an add-on package due to package problems. gui.messageBox( _("Cannot update {name} - missing file or invalid file format" ).format(name=self.addonName), translate("Error"), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return # Check compatibility with NVDA and/or Windows release. import versionInfo minimumNVDAVersion = bundle.manifest.get("minimumNVDAVersion", None) if minimumNVDAVersion is None: minimumNVDAVersion = [ versionInfo.version_year, versionInfo.version_major ] lastTestedNVDAVersion = bundle.manifest.get( "lastTestedNVDAVersion", None) if lastTestedNVDAVersion is None: lastTestedNVDAVersion = [ versionInfo.version_year, versionInfo.version_major ] # For NVDA version, only version_year.version_major will be checked. minimumYear, minimumMajor = minimumNVDAVersion[:2] lastTestedYear, lastTestedMajor = lastTestedNVDAVersion[:2] if not ((minimumYear, minimumMajor) <= (versionInfo.version_year, versionInfo.version_major) <= (lastTestedYear, lastTestedMajor)): # Translators: The message displayed when trying to update an add-on that is not going to be compatible with the current version of NVDA. gui.messageBox( _("{name} add-on is not compatible with this version of NVDA. Minimum NVDA version: {minYear}.{minMajor}, last tested: {testedYear}.{testedMajor}." ).format(name=self.addonName, minYear=minimumYear, minMajor=minimumMajor, testedYear=lastTestedYear, testedMajor=lastTestedMajor), translate("Error"), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return # Some add-ons require a specific Windows release or later. import winVersion minimumWindowsVersion = bundle.manifest.get( "minimumWindowsVersion", None) if minimumWindowsVersion is None: minimumWindowsVersion = winVersion.winVersion[:3] else: minimumWindowsVersion = [ int(data) for data in minimumWindowsVersion.split(".") ] minimumWinMajor, minimumWinMinor, minimumWinBuild = minimumWindowsVersion winMajor, winMinor, winBuild = winVersion.winVersion[:3] if (winMajor, winMinor, winBuild) < ( minimumWinMajor, minimumWinMinor, minimumWinBuild): # Translators: The message displayed when the add-on requires a newer version of Windows. gui.messageBox( _("{name} add-on is not compatible with this version of Windows." ).format(name=self.addonName), translate("Error"), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return bundleName = bundle.manifest['name'] isDisabled = False # Optimization (future): it is better to remove would-be add-ons all at once instead of doing it each time a bundle is opened. for addon in addonHandler.getAvailableAddons(): # Check for disabled state first. if bundleName == addon.manifest['name']: if addon.isDisabled: isDisabled = True if not addon.isPendingRemove: addon.requestRemove() break progressDialog = gui.IndeterminateProgressDialog( gui.mainFrame, # Translators: The title of the dialog presented while an Addon is being updated. _("Updating {name}").format(name=self.addonName), # Translators: The message displayed while an addon is being updated. _("Please wait while the add-on is being updated.")) try: gui.ExecAndPump(addonHandler.installAddonBundle, bundle) except: log.error("Error installing addon bundle from %s" % self.destPath, exc_info=True) progressDialog.done() progressDialog.Hide() progressDialog.Destroy() # Translators: The message displayed when an error occurs when installing an add-on package. gui.messageBox( _("Failed to update {name} add-on").format( name=self.addonName), translate("Error"), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return else: progressDialog.done() progressDialog.Hide() progressDialog.Destroy() _updatedAddons.append(bundleName) if isDisabled: for addon in addonHandler.getAvailableAddons(): if bundleName == addon.manifest[ 'name'] and addon.isPendingInstall: addon.enable(False) break finally: try: os.remove(self.destPath) except OSError: pass self.continueUpdatingAddons()
def installAddon(storeClient, addon, closeAfter=False, silent=False): if silent == False: ui.message(_("Downloading %s") % (addon.name)) data = storeClient.getAddonFile(addon.id, addon.versionId) if data is None: if silent == False: ui.message(_("Unable to download the add-on.")) return False tmp = os.path.join(config.getUserDefaultConfigPath(), "storeDownloadedAddon.nvda-addon") logHandler.log.info(u"Saving to %s" % (tmp)) f = file(tmp, "wb") f.write(data) f.close() path = tmp if path is None: if silent == False: ui.message(_("Unable to download %s") % (addon.name)) return False if silent == False: ui.message(_("Installing")) try: bundle = addonHandler.AddonBundle(path) except: logHandler.log.error("Error opening addon bundle from %s" % path, exc_info=True) # Translators: The message displayed when an error occurs when opening an add-on package for adding. if silent == False: gui.messageBox( _("Failed to open add-on package file at %s - missing file or invalid file format" ) % path, # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return False bundleName = bundle.manifest['name'] prevAddon = None for addon in addonHandler.getAvailableAddons(): if not addon.isPendingRemove and bundleName == addon.manifest['name']: prevAddon = addon break if prevAddon: prevAddon.requestRemove() if silent is False: progressDialog = gui.IndeterminateProgressDialog( gui.mainFrame, # Translators: The title of the dialog presented while an Addon is being installed. _("Installing Add-on"), # Translators: The message displayed while an addon is being installed. _("Please wait while the add-on is being installed.")) try: gui.ExecAndPump(addonHandler.installAddonBundle, bundle) except: logHandler.log.error("Error installing addon bundle from %s" % addonPath, exc_info=True) progressDialog.done() del progressDialog # Translators: The message displayed when an error occurs when installing an add-on package. gui.messageBox( _("Failed to install add-on from %s") % (addon.name), # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return False progressDialog.done() del progressDialog else: try: addonHandler.installAddonBundle(bundle) except: return False if closeAfter: wx.CallLater(1, core.restart) return True
def _downloadSuccess(self): self._stopped() # Emulate add-on update (don't prompt to install). from gui import addonGui closeAfter = addonGui.AddonsDialog._instance is None try: try: bundle = addonHandler.AddonBundle(self.destPath.decode("mbcs")) except: log.error("Error opening addon bundle from %s" % self.destPath, exc_info=True) # Translators: The message displayed when an error occurs when opening an add-on package for adding. gui.messageBox( _("Failed to open add-on package file at %s - missing file or invalid file format" ) % self.destPath, # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return bundleName = bundle.manifest['name'] for addon in addonHandler.getAvailableAddons(): if not addon.isPendingRemove and bundleName == addon.manifest[ 'name']: addon.requestRemove() break progressDialog = gui.IndeterminateProgressDialog( gui.mainFrame, # Translators: The title of the dialog presented while an Addon is being updated. _("Updating Add-on"), # Translators: The message displayed while an addon is being updated. _("Please wait while the add-on is being updated.")) try: gui.ExecAndPump(addonHandler.installAddonBundle, bundle) except: log.error("Error installing addon bundle from %s" % self.destPath, exc_info=True) if not closeAfter: addonGui.AddonsDialog(gui.mainFrame).refreshAddonsList() progressDialog.done() del progressDialog # Translators: The message displayed when an error occurs when installing an add-on package. gui.messageBox( _("Failed to update add-on from %s") % self.destPath, # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return else: if not closeAfter: addonGui.AddonsDialog( gui.mainFrame).refreshAddonsList(activeIndex=-1) progressDialog.done() del progressDialog finally: try: os.remove(self.destPath) except OSError: pass if closeAfter: wx.CallLater(1, addonGui.AddonsDialog(gui.mainFrame).Close)
def _downloadSuccess(self): self._stopped() try: try: bundle = addonHandler.AddonBundle(self.destPath) except: log.error(f"Error opening addon bundle from {self.destPath}", exc_info=True) gui.messageBox( # Translators: The message displayed when an error occurs # when trying to update an add-on package due to package problems. _("Cannot update {name} - missing file or invalid file format" ).format(name=self.addonName), translate("Error"), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return # NVDA itself will check add-on compatibility range. # As such, the below fragment was borrowed from NVDA Core (credit: NV Access). from addonHandler import addonVersionCheck from gui import addonGui if not addonVersionCheck.hasAddonGotRequiredSupport(bundle): addonGui._showAddonRequiresNVDAUpdateDialog( gui.mainFrame, bundle) self.continueUpdatingAddons() return elif not addonVersionCheck.isAddonTested(bundle): addonGui._showAddonTooOldDialog(gui.mainFrame, bundle) self.continueUpdatingAddons() return bundleName = bundle.manifest['name'] isDisabled = False # Optimization (future): it is better to remove would-be add-ons all at once # instead of doing it each time a bundle is opened. for addon in addonHandler.getAvailableAddons(): # Check for disabled state first. if bundleName == addon.manifest['name']: if addon.isDisabled: isDisabled = True if not addon.isPendingRemove: addon.requestRemove() break progressDialog = gui.IndeterminateProgressDialog( gui.mainFrame, # Translators: The title of the dialog presented while an Addon is being updated. _("Updating {name}").format(name=self.addonName), # Translators: The message displayed while an addon is being updated. _("Please wait while the add-on is being updated.")) try: gui.ExecAndPump(addonHandler.installAddonBundle, bundle) except: log.error( f"Error installing addon bundle from {self.destPath}", exc_info=True) progressDialog.done() progressDialog.Hide() progressDialog.Destroy() gui.messageBox( # Translators: The message displayed when an error occurs when installing an add-on package. _("Failed to update {name} add-on").format( name=self.addonName), translate("Error"), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return else: progressDialog.done() progressDialog.Hide() progressDialog.Destroy() _updatedAddons.append(bundleName) if isDisabled: for addon in addonHandler.getAvailableAddons(): if bundleName == addon.manifest[ 'name'] and addon.isPendingInstall: addon.enable(False) break finally: try: os.remove(self.destPath) except OSError: pass self.continueUpdatingAddons()
def installAddon(self, addonPath, closeAfter=False): try: try: bundle = addonHandler.AddonBundle(addonPath) except: log.error("Error opening addon bundle from %s" % addonPath, exc_info=True) gui.messageBox( # Translators: The message displayed when an error occurs when opening an add-on package for adding. _("Failed to open add-on package file at %s - missing file or invalid file format" ) % addonPath, # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return if not addonVersionCheck.hasAddonGotRequiredSupport(bundle): self._showAddonRequiresNVDAUpdateDialog(bundle) return if not addonVersionCheck.isAddonTested(bundle): if wx.YES != self._showAddonUntestedDialog(bundle): return AddonCompatibilityState.setAddonCompatibility( addon=bundle, compatibilityStateValue=compatValues. MANUALLY_SET_COMPATIBLE) elif wx.YES != self._showConfirmAddonInstallDialog(bundle): return prevAddon = None for addon in self.curAddons: if not addon.isPendingRemove and bundle.name == addon.manifest[ 'name']: prevAddon = addon break if prevAddon: summary = bundle.manifest["summary"] curVersion = prevAddon.manifest["version"] newVersion = bundle.manifest["version"] if gui.messageBox( # Translators: A message asking if the user wishes to update an add-on with the same version currently installed according to the version number. _("You are about to install version {newVersion} of {summary}, which appears to be already installed. Would you still like to update?" ).format(summary=summary, newVersion=newVersion) if curVersion == newVersion else # Translators: A message asking if the user wishes to update a previously installed add-on with this one. _("A version of this add-on is already installed. Would you like to update {summary} version {curVersion} to version {newVersion}?" ).format(summary=summary, curVersion=curVersion, newVersion=newVersion), # Translators: A title for the dialog asking if the user wishes to update a previously installed add-on with this one. _("Add-on Installation"), wx.YES | wx.NO | wx.ICON_WARNING) != wx.YES: return prevAddon.requestRemove() progressDialog = gui.IndeterminateProgressDialog( gui.mainFrame, # Translators: The title of the dialog presented while an Addon is being installed. _("Installing Add-on"), # Translators: The message displayed while an addon is being installed. _("Please wait while the add-on is being installed.")) try: gui.ExecAndPump(addonHandler.installAddonBundle, bundle) except: log.error("Error installing addon bundle from %s" % addonPath, exc_info=True) self.refreshAddonsList() progressDialog.done() del progressDialog # Translators: The message displayed when an error occurs when installing an add-on package. gui.messageBox( _("Failed to install add-on from %s") % addonPath, # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) return else: self.refreshAddonsList(activeIndex=-1) progressDialog.done() del progressDialog finally: if closeAfter: # #4460: If we do this immediately, wx seems to drop the WM_QUIT sent if the user chooses to restart. # This seems to have something to do with the wx.ProgressDialog. # The CallLater seems to work around this. wx.CallLater(1, self.Close)
def _downloadSuccess(self): self._stopped() try: try: bundle = addonHandler.AddonBundle(self.destPath) except: log.error(f"Error opening addon bundle from {self.destPath}", exc_info=True) gui.messageBox( # Translators: The message displayed when an error occurs # when trying to update an add-on package due to package problems. _("Cannot update {name} - missing file or invalid file format" ).format(name=self.addonName), translate("Error"), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return # Check compatibility with NVDA and/or Windows release. # NVDA itself will check add-on compatibility range. # As such, the below fragment was borrowed from NVDA Core (credit: NV Access). from addonHandler import addonVersionCheck from gui import addonGui # Check compatibility with NVDA and/or Windows release. if not addonVersionCheck.hasAddonGotRequiredSupport(bundle): addonGui._showAddonRequiresNVDAUpdateDialog( gui.mainFrame, bundle) self.continueUpdatingAddons() return elif not addonVersionCheck.isAddonTested(bundle): addonGui._showAddonTooOldDialog(gui.mainFrame, bundle) self.continueUpdatingAddons() return # Some add-ons require a specific Windows release or later. # Prepare for winVersion.getWinVer function. import winVersion minimumWindowsVersion = bundle.manifest.get( "minimumWindowsVersion", None) if hasattr(winVersion, "getWinVer"): if minimumWindowsVersion is None: minimumWindowsVersion = winVersion.getWinVer() else: minimumWindowsVersion = winVersion.WinVersion.fromVersionText( minimumWindowsVersion) winVersionUnsupported = winVersion.getWinVer( ) < minimumWindowsVersion else: if minimumWindowsVersion is None: minimumWindowsVersion = winVersion.winVersion[:3] else: minimumWindowsVersion = [ int(data) for data in minimumWindowsVersion.split(".") ] minimumWinMajor, minimumWinMinor, minimumWinBuild = minimumWindowsVersion winMajor, winMinor, winBuild = winVersion.winVersion[:3] winVersionUnsupported = (winMajor, winMinor, winBuild) < ( minimumWinMajor, minimumWinMinor, minimumWinBuild) if winVersionUnsupported: gui.messageBox( # Translators: The message displayed when the add-on requires a newer version of Windows. _("{name} add-on is not compatible with this version of Windows." ).format(name=self.addonName), translate("Error"), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return bundleName = bundle.manifest['name'] isDisabled = False # Optimization (future): it is better to remove would-be add-ons all at once # instead of doing it each time a bundle is opened. for addon in addonHandler.getAvailableAddons(): # Check for disabled state first. if bundleName == addon.manifest['name']: if addon.isDisabled: isDisabled = True if not addon.isPendingRemove: addon.requestRemove() break progressDialog = gui.IndeterminateProgressDialog( gui.mainFrame, # Translators: The title of the dialog presented while an Addon is being updated. _("Updating {name}").format(name=self.addonName), # Translators: The message displayed while an addon is being updated. _("Please wait while the add-on is being updated.")) try: gui.ExecAndPump(addonHandler.installAddonBundle, bundle) except: log.error( f"Error installing addon bundle from {self.destPath}", exc_info=True) progressDialog.done() progressDialog.Hide() progressDialog.Destroy() gui.messageBox( # Translators: The message displayed when an error occurs when installing an add-on package. _("Failed to update {name} add-on").format( name=self.addonName), translate("Error"), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return else: progressDialog.done() progressDialog.Hide() progressDialog.Destroy() _updatedAddons.append(bundleName) if isDisabled: for addon in addonHandler.getAvailableAddons(): if bundleName == addon.manifest[ 'name'] and addon.isPendingInstall: addon.enable(False) break finally: try: os.remove(self.destPath) except OSError: pass self.continueUpdatingAddons()