def refreshAddonsList(self,activeIndex=0): self.addonsList.DeleteAllItems() self.curAddons=[] shouldEnableIncompatAddonsButton = False for addon in addonHandler.getAvailableAddons(): self.addonsList.Append(( addon.manifest['summary'], self.getAddonStatus(addon), addon.manifest['version'], addon.manifest['author'] )) self.curAddons.append(addon) shouldEnableIncompatAddonsButton = ( shouldEnableIncompatAddonsButton or ( addonVersionCheck.hasAddonGotRequiredSupport(addon, version=addonVersionCheck.CURRENT_NVDA_VERSION) and not addonVersionCheck.isAddonTested(addon, version=addonVersionCheck.CURRENT_NVDA_VERSION) ) ) self.incompatAddonsButton.Enable(shouldEnableIncompatAddonsButton) # select the given active addon or the first addon if not given curAddonsLen=len(self.curAddons) if curAddonsLen>0: if activeIndex==-1: activeIndex=curAddonsLen-1 elif activeIndex<0 or activeIndex>=curAddonsLen: activeIndex=0 self.addonsList.Select(activeIndex,on=1) self.addonsList.SetItemState(activeIndex,wx.LIST_STATE_FOCUSED,wx.LIST_STATE_FOCUSED) else: self.aboutButton.Disable() self.helpButton.Disable() self.removeButton.Disable()
def refreshAddonsList(self,activeIndex=0): self.addonsList.DeleteAllItems() self.curAddons=[] anyAddonIncompatible = False for addon in addonHandler.getAvailableAddons(): self.addonsList.Append(( addon.manifest['summary'], self.getAddonStatus(addon), addon.manifest['version'], addon.manifest['author'] )) self.curAddons.append(addon) anyAddonIncompatible = ( anyAddonIncompatible # once we find one incompatible addon we don't need to continue or not addonVersionCheck.isAddonCompatible( addon, currentAPIVersion=addonAPIVersion.CURRENT, backwardsCompatToVersion=addonAPIVersion.BACK_COMPAT_TO ) ) self.incompatAddonsButton.Enable(anyAddonIncompatible) # select the given active addon or the first addon if not given curAddonsLen=len(self.curAddons) if curAddonsLen>0: if activeIndex==-1: activeIndex=curAddonsLen-1 elif activeIndex<0 or activeIndex>=curAddonsLen: activeIndex=0 self.addonsList.Select(activeIndex,on=1) self.addonsList.SetItemState(activeIndex,wx.LIST_STATE_FOCUSED,wx.LIST_STATE_FOCUSED) else: self.aboutButton.Disable() self.helpButton.Disable() self.removeButton.Disable()
def onInstall(): for addon in addonHandler.getAvailableAddons(): if addon.manifest['name'] == "EloquenceAutoLanguageSwitching": gui.messageBox( "You have an older version of Eloquence Auto Language Switching installed, it is going to be uninstalled now to avoid compatibility issues.", "Older version installed", wx.ICON_WARNING) addon.requestRemove()
def onInstall(): for addon in addonHandler.getAvailableAddons(): if addon.manifest['name'] == "CalibreNvda": if gui.messageBox( # Translators: the label of a message box dialog. _("You have installed an old and incompatible version of this add-on. Do you want to uninstall the old version?"), # Translators: the title of a message box dialog. _("Uninstall old add-on"), wx.YES|wx.NO|wx.ICON_WARNING) == wx.YES: addon.requestRemove() break
def afterDialog(res): # here we need to check if the compatibility has changed. # addons that have become incompat should be disabled, and a restart prompt shown # addons that have become compat should be visible, but not enabled unless they already were. from addonHandler.compatValues import MANUALLY_SET_INCOMPATIBLE getAddonCompatibility = AddonCompatibilityState.getAddonCompatibility manuallySetIncompatibleAddons = addonHandler.getAvailableAddons( filterFunc=lambda addon: ( MANUALLY_SET_INCOMPATIBLE == getAddonCompatibility(addon, CURRENT_NVDA_VERSION)) ) for addon in manuallySetIncompatibleAddons: addon.enable(shouldEnable=False) self.refreshAddonsList()
def refreshAddonsList(self,activeIndex=0): self.addonsList.DeleteAllItems() self.curAddons=[] for addon in addonHandler.getAvailableAddons(): self.addonsList.Append((addon.manifest['summary'], self.getAddonStatus(addon), addon.manifest['version'], addon.manifest['author'])) self.curAddons.append(addon) # select the given active addon or the first addon if not given curAddonsLen=len(self.curAddons) if curAddonsLen>0: if activeIndex==-1: activeIndex=curAddonsLen-1 elif activeIndex<0 or activeIndex>=curAddonsLen: activeIndex=0 self.addonsList.Select(activeIndex,on=1) self.addonsList.SetItemState(activeIndex,wx.LIST_STATE_FOCUSED,wx.LIST_STATE_FOCUSED) else: self.aboutButton.Disable() self.removeButton.Disable()
def onInstall(): for addon in addonHandler.getAvailableAddons(): if addon.manifest['name'] == "zRadio": dirOrigen =os.path.join(globalVars.appArgs.configPath) dirDestino =os.path.join(globalVars.appArgs.configPath, "zRadio") fileOptions = os.path.join(dirOrigen, "opciones.dat") fileOptionsRadio = os.path.join(dirOrigen, "opt_radio.dat") fileFavRadio = os.path.join(dirOrigen, "fav_radios.dat") if os.path.exists(dirDestino): pass else: os.mkdir(os.path.join(dirOrigen, "zRadio")) if os.path.isfile(fileOptions): shutil.move(fileOptions, os.path.join(dirDestino, "opciones.dat")) if os.path.isfile(fileOptionsRadio): shutil.move(fileOptionsRadio, os.path.join(dirDestino, "opt_radio.dat")) if os.path.isfile(fileFavRadio): shutil.move(fileFavRadio, os.path.join(dirDestino, "fav_radios.dat")) break
def refreshAddonsList(self, activeIndex=0): self.addonsList.DeleteAllItems() self.curAddons = [] for addon in addonHandler.getAvailableAddons(): self.addonsList.Append( (addon.manifest['summary'], self.getAddonStatus(addon), addon.manifest['version'], addon.manifest['author'])) self.curAddons.append(addon) # select the given active addon or the first addon if not given curAddonsLen = len(self.curAddons) if curAddonsLen > 0: if activeIndex == -1: activeIndex = curAddonsLen - 1 elif activeIndex < 0 or activeIndex >= curAddonsLen: activeIndex = 0 self.addonsList.Select(activeIndex, on=1) self.addonsList.SetItemState(activeIndex, wx.LIST_STATE_FOCUSED, wx.LIST_STATE_FOCUSED) else: self.aboutButton.Disable() self.removeButton.Disable()
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 __init__(self): """Initializes the global plugin object.""" super(globalPluginHandler.GlobalPlugin, self).__init__() global _nvdaGetPropertiesSpeech, _nvdaSpeak, _gpObject # if on a secure Desktop, disable the Add-on if globalVars.appArgs.secure: return _gpObject = self try: self.language = config.conf["general"]["language"] except: self.language = None pass if self.language is None or self.language == 'Windows': try: self.language = languageHandler.getWindowsLanguage()[:2] except: self.language = 'en' self.updater = updater.ExtensionUpdater() self.updater.start() self.inTimer = False self.hasBeenUpdated = False wx.CallLater(1000, self.onTimer) import addonHandler version = None for addon in addonHandler.getAvailableAddons(): if addon.name == "translate": version = addon.version if version is None: version = 'unknown' logHandler.log.info("Translate (%s) initialized, translating to %s" % (version, self.language)) _nvdaSpeak = speech._manager.speak _nvdaGetPropertiesSpeech = speech.getPropertiesSpeech speech._manager.speak = speak speech.getPropertiesSpeech = _nvdaGetPropertiesSpeech self.loadLocalCache()
def onInstall(postPathBug = False): #Add ourself to the path, so that commands when spoken can be queried to us. #Only if we are truely installing though. addons = [] if not postPathBug: addons = addonHandler.getAvailableAddons() for addon in addons: if addon.name=="DictationBridge": #Hack to work around condition where #the uninstaller removes this addon from the path #After the installer for the updator ran. #We could use version specific directories, but wsr macros Does not #play nice with refreshing the path environment after path updates, # requiring a complete reboot of wsr, or commands spontaneously break cripticly. with open(os.path.join(config.getUserDefaultConfigPath(), ".dbInstall"), "w") as fi: fi.write("dbInstall") return key = _winreg.OpenKeyEx(_winreg.HKEY_CURRENT_USER, "Environment", 0, _winreg.KEY_READ | _winreg.KEY_WRITE) try: value, typ = _winreg.QueryValueEx(key, "Path") except: value, typ = None, _winreg.REG_EXPAND_SZ if value is None: value = "" dir = os.path.dirname(__file__) if not isinstance(dir, unicode): dir = dir.decode(sys.getfilesystemencoding()) dir = dir.replace(addonHandler.ADDON_PENDINGINSTALL_SUFFIX, "") log.info("addon directory: %r" % dir) log.info("current PATH: %r" % value) if value.lower().find(dir.lower()) == -1: if value != "": value += ";" value += dir log.info("new PATH: %r" % value) _winreg.SetValueEx(key, "Path", None, typ, value) sendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, u"Environment")
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 checkForAddonUpdates(): curAddons = {} addonSummaries = {} for addon in addonHandler.getAvailableAddons(): # Sorry Nuance Vocalizer family, no update checks for you. if "vocalizer" in addon.name.lower(): continue manifest = addon.manifest name = addon.name if name in addonUtils.updateState["noUpdates"]: continue curVersion = manifest["version"] # Check different channels if appropriate. updateChannel = manifest.get("updateChannel") if updateChannel == "None": updateChannel = None if updateChannel != "dev" and name in addonUtils.updateState["devUpdates"]: updateChannel = "dev" elif updateChannel == "dev" and name not in addonUtils.updateState["devUpdates"]: updateChannel = None curAddons[name] = {"summary": manifest["summary"], "version": curVersion, "channel": updateChannel} addonSummaries[name] = manifest["summary"] try: info = checkForAddonUpdate(curAddons) except: info = {} #data = json.dumps(curAddons) # Pseudocode: """try: res = urllib.open(someURL, data) # Check SSL and what not. res = json.loads(res)""" #res = json.loads(data) res = info for addon in res: res[addon]["summary"] = addonSummaries[addon] # In reality, it'll be a list of URL's to try. res[addon]["urls"] = res[addon]["path"] return res if len(res) else None
def getAddonsList(self): from locale import strxfrm if py3: return sorted(addonHandler.getAvailableAddons(), key=lambda a: strxfrm(a.manifest['summary'])) else: return sorted(addonHandler.getAvailableAddons(), key=lambda a: strxfrm(a.manifest['summary'].encode("mbcs")))
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 onInstall(): for addon in addonHandler.getAvailableAddons(): if addon.manifest['name'] == "remote" and addon.manifest[ 'version'].endswith("+"): askToRemove(addon) break
def shouldNotUpdate(): # Returns a list of descriptions for add-ons that should not update. return [addon.manifest["summary"] for addon in addonHandler.getAvailableAddons() if addon.name in addonUtils.updateState["noUpdates"]]
def getAddonsList(self): from locale import strxfrm return sorted(addonHandler.getAvailableAddons(), key=lambda a: strxfrm(a.manifest['summary']))
def updateAddonDic(self, addonDic): """ This method makes it possible to create a dictionary grouping all the add-ons that contain scripts associated or not with gestures. """ import addonHandler # We gather all the gestures available in a dictionary, thanks to inputCore.manager.getAllGestureMappings. allGest = inputCore.manager.getAllGestureMappings( obj=gui.mainFrame.prevFocus, ancestors=gui.mainFrame.prevFocusAncestors) # We store the list of installed add-ons in a variable named addonsList. addonsList = [addon for addon in addonHandler.getAvailableAddons()] # We Iterates through the dictionary containing all the available gestures. for category in allGest: # We create a local variable, storing the value of each of the main items in our dictionary. command = allGest[category] # These items represent the different categories available. # The values retrieved by allGest[category], in turn comprise another dictionary grouping the description of each of the scripts in each category. # We Iterates among the dictionary objects for each of the categories. for doc in command: # Each item of its sub-dictionaries represents the documentation of each of the scripts, which is why it was preferably named doc. # We now create a local variable script, which will retrieve each of the values of our sub-dictionary. script = command[doc] # each of its values is an instance of the inputCore.AllGesturesScriptInfo class for each of the scripts. # It's an object representing a script, which is why it was preferably named script. # We check if our script is that of an add-on, we will make further checks below. if script.moduleName.startswith( "globalPlugins") or script.moduleName.startswith( "appModules"): # The moduleName property of each of these script objects returns the name of the module that contains the script. # Now, it's going to be a bit complicated. # We point to the script object directly in the appModule or globalPlugin, to check its path. scriptMod = getattr( script.cls, "script_{scriptName}".format( scriptName=script.scriptName), None) if scriptMod: # Here is the path to the module, it will be interesting, because it's it that will reveal if it's an add-on or not. modPath = scriptMod.im_func.func_code.co_filename # This is the only way we have to retrieve the name of the add-on. # We check the presence of the "addons" directory in the path. if any(addon.path in modPath for addon in addonsList): # There is no longer any doubt, it's an add-on. # We get the summary of the add-on. addonSum = [ addon.manifest["summary"] for addon in addonsList if addon.path in modPath ][0] # We check the gesture (s) of our script. if len(script.gestures) > 0: gestInfo = " | ".join([ self.adjustGesture(x) for x in script.gestures ]) else: #. Translators: Message to inform there are no command assigned. gestInfo = _( "Not assigned to gesture or part of layered commands" ) # We try to update our dictionary addonDic, according to whether it has taken knowledge of each item or not. try: addonDic[addonSum][ gestInfo] = script.displayName except KeyError: # It has not yet read it, so we create the very first sub-dictionary representing each of our scripts contained in each of our add-ons. addonDic[addonSum] = {} # We can now make our update. addonDic[addonSum][ gestInfo] = script.displayName
if not versionInfo.updateVersionType: raise RuntimeError( "NVDA is running from source code, add-on update check is not supported" ) # NVDA 2018.1 and later. import config if config.isAppX: raise RuntimeError("This is NVDA Windows Store edition") import addonHandler addonHandler.initTranslation() # Provided that NVDA issue 3208 is implemented. if hasattr(addonHandler, "checkForAddonUpdate"): raise RuntimeError("NVDA itself will check for add-on updates") # Temporary: check of Add-on Updater add-on is running. for addon in addonHandler.getAvailableAddons(): if (addon.name, addon.isDisabled) == ("addonUpdater", False): raise RuntimeError("Another add-on update provider exists") import sys py3 = sys.version.startswith("3") import os # Essentially, update download is no different than file downloads. import time if py3: import pickle from urllib.request import urlopen else: import cPickle as pickle from urllib import urlopen import threading
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()
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() 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 uninstallPreviousVersion(): for addon in addonHandler.getAvailableAddons(): if ( addon.manifest["name"], addon.manifest["author"]) == previousNameAndAuthor: addon.requestRemove() break
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 getLocalAddon(self, storeAddon): for a in addonHandler.getAvailableAddons(): if a.manifest['name'].upper() == storeAddon.name.upper(): return a return None
def __getStores(self): for addon in addonHandler.getAvailableAddons(): # Introduced in NVDA 2016.3 if hasattr(addon, "isDisabled") and addon.isDisabled: continue yield self.addonStoreFactory(addon)
def preferDevUpdates(): # Returns a list of descriptions for add-ons that prefers development releases. return [addon.manifest["summary"] for addon in addonHandler.getAvailableAddons() if addon.name in addonUtils.updateState["devUpdates"]]
# import the necessary modules. import globalPluginHandler import addonHandler import gui import globalVars import wx import shutil import os import pickle from threading import Thread # For translation addonHandler.initTranslation() # List containing all the add-ons lista = list(addonHandler.getAvailableAddons()) # Creation of a GlobalPlugin class, derived from globalPluginHandler.GlobalPlugin. class GlobalPlugin(globalPluginHandler.GlobalPlugin): # Creating the constructor of the newly created GlobalPlugin class. def __init__(self): # Call of the constructor of the parent class. super(GlobalPlugin, self).__init__() self._MainWindows = None if globalVars.appArgs.secure: return # Creation of our menu. self.toolsMenu = gui.mainFrame.sysTrayIcon.toolsMenu
def _downloadSuccess(self): def getBundle(): try: bundle = addonHandler.AddonBundle(self.destPath.decode("mbcs")) except AttributeError: bundle = addonHandler.AddonBundle(self.destPath) except Exception: 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( # Translators: message to user _("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 self._stopped() try: bundle = getBundle() if bundle is None: self.continueUpdatingAddons() return minimumNVDAVersion = bundle.manifest.get("minimumNVDAVersion", None) lastTestedNVDAVersion = bundle.manifest.get( "lastTestedNVDAVersion", None) bundleName = bundle.manifest['name'] if not checkCompatibility(bundleName, minimumNVDAVersion, lastTestedNVDAVersion): self.continueUpdatingAddons() return 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. makeAddonWindowTitle(_("Updating")), # Translators: The message displayed while an addon is being updated. _("Please wait while the add-on is being updated.")) try: if self.autoUpdate: extraAppArgs = globalVars.appArgsExtra if hasattr( globalVars, "appArgsExtra") else globalVars.unknownAppArgs extraAppArgs.append("addon-auto-update") gui.ExecAndPump(addonHandler.installAddonBundle, bundle) if self.autoUpdate: extraAppArgs.remove("addon-auto-update") except Exception: if self.autoUpdate: extraAppArgs.remove("addon-auto-update") log.error("Error installing addon bundle from %s" % self.destPath, exc_info=True) progressDialog.done() 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), makeAddonWindowTitle(NVDAString("Error")), wx.OK | wx.ICON_ERROR) self.continueUpdatingAddons() return else: progressDialog.done() self.addonHasBeenUpdated = True 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 if self.addonHasBeenUpdated: if gui.messageBox( NVDAString( "Changes were made to add-ons. " "You must restart NVDA for these changes to take effect. Would you like to restart now?" ), NVDAString("Restart NVDA"), wx.YES | wx.NO | wx.ICON_WARNING) == wx.YES: wx.CallAfter(core.restart) return self.continueUpdatingAddons()
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 onInstall(): for addon in addonHandler.getAvailableAddons(): if addon.manifest['name'] == "Unspoken Py3": askToRemove(addon) break