Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to specify add-on store metadata URL from within NVDA #17099

Merged
merged 34 commits into from
Sep 5, 2024
Merged
Show file tree
Hide file tree
Changes from 16 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
ca63806
Created `addonStoore.network._getBaseUrl` method that currently just …
SaschaCowley Aug 28, 2024
c0aee14
Added a new config item, `addonStore.baseURL`, to store configurable …
SaschaCowley Aug 28, 2024
c4939be
Add mirror option to settings
SaschaCowley Aug 28, 2024
0cd1c06
Start implementation of URL validator for config
SaschaCowley Aug 28, 2024
dd591ab
Refactored DisplayableError calling code into its own private function
SaschaCowley Aug 30, 2024
57a35a1
Update getAllAddons for refactor
SaschaCowley Aug 30, 2024
7336874
Made URL getters more robust
SaschaCowley Aug 30, 2024
23b3b69
Moved helper function down
SaschaCowley Sep 2, 2024
74adc96
Added helpful messages
SaschaCowley Sep 2, 2024
19714c8
Fixed case
SaschaCowley Sep 2, 2024
7e1af57
Merge branch 'master' into addonStoreMirror
SaschaCowley Sep 2, 2024
5eaf961
Added change log entry
SaschaCowley Sep 2, 2024
7e00e7f
Renamed settings controls
SaschaCowley Sep 2, 2024
c31aced
Added documentation
SaschaCowley Sep 2, 2024
0d2a613
Renamed `BASE_URL` to `_DEFAULT_BASE_URL`
SaschaCowley Sep 2, 2024
170464e
Added note to changes for developers
SaschaCowley Sep 2, 2024
3937db0
Apply suggestions from code review
SaschaCowley Sep 3, 2024
c59d7da
Renamed config key to be more descriptive
SaschaCowley Sep 3, 2024
7a5ca61
Removed un-used config.validators file
SaschaCowley Sep 3, 2024
add7e34
Changed mirror url label
SaschaCowley Sep 3, 2024
a409b51
Improved formatting
SaschaCowley Sep 3, 2024
4929e07
Changed wording of error message to exclude 'metadata' and include th…
SaschaCowley Sep 3, 2024
ddd4492
Updated handling of None type for _do_displayError for greater predic…
SaschaCowley Sep 3, 2024
7e3d2e4
Partial implementation of URL validation
SaschaCowley Sep 3, 2024
716dc9d
Updated validation
SaschaCowley Sep 3, 2024
723775d
Removed unused showTip option
SaschaCowley Sep 3, 2024
9c2ffb1
Replaced custom logic with logic from url_normalize
SaschaCowley Sep 3, 2024
70923f1
Simplified generation of Add-on Store URLs now that we have more cert…
SaschaCowley Sep 3, 2024
94fe401
Apply suggestions from code review
SaschaCowley Sep 3, 2024
3ed8206
Moved _stripAccelleratorFromLabel to guiHelper
SaschaCowley Sep 4, 2024
e25076e
Added explicit dependency on url-normalize
SaschaCowley Sep 4, 2024
183a6f6
Removed unused function
SaschaCowley Sep 5, 2024
eb3afba
Updated label of Add-on Store metadata mirror URL box
SaschaCowley Sep 5, 2024
44d9de7
Merge branch 'master' into addonStoreMirror
SaschaCowley Sep 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 41 additions & 12 deletions source/addonStore/dataManager.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,16 @@ def _getCachedAddonData(self, cacheFilePath: str) -> Optional[CachedAddonsModel]

# Translators: A title of the dialog shown when fetching add-on data from the store fails
_updateFailureMessage = pgettext("addonStore", "Add-on data update failure")
_updateFailureMirrorSuggestion = pgettext(
"addonStore",
# Translators: A suggestion of what to do when fetching add-on data from the store fails and a metadata mirror is being used.
"Make sure you are connected to the internet, and the add-on store metadata mirror URL is valid.",
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved
)
_updateFailureDefaultSuggestion = pgettext(
"addonStore",
# Translators: A suggestion of what to do when fetching add-on data from the store fails and the default metadata URL is being used.
"Make sure you are connected to the internet and try again.",
)

def getLatestCompatibleAddons(
self,
Expand Down Expand Up @@ -234,15 +244,12 @@ def getLatestCompatibleAddons(
cachedLanguage=self._lang,
nvdaAPIVersion=addonAPIVersion.CURRENT,
)
elif onDisplayableError is not None:
from gui.message import DisplayableError

displayableError = DisplayableError(
else:
self._do_displayError(
onDisplayableError,
# Translators: A message shown when fetching add-on data from the store fails
pgettext("addonStore", "Unable to fetch latest add-on data for compatible add-ons."),
self._updateFailureMessage,
)
callLater(delay=0, callable=onDisplayableError.notify, displayableError=displayableError)

if self._compatibleAddonCache is None:
return _createAddonGUICollection()
Expand Down Expand Up @@ -273,20 +280,42 @@ def getLatestAddons(
cachedLanguage=self._lang,
nvdaAPIVersion=_LATEST_API_VER,
)
elif onDisplayableError is not None:
from gui.message import DisplayableError

displayableError = DisplayableError(
else:
self._do_displayError(
onDisplayableError,
# Translators: A message shown when fetching add-on data from the store fails
pgettext("addonStore", "Unable to fetch latest add-on data for incompatible add-ons."),
self._updateFailureMessage,
)
callLater(delay=0, callable=onDisplayableError.notify, displayableError=displayableError)

if self._latestAddonCache is None:
return _createAddonGUICollection()
return deepcopy(self._latestAddonCache.cachedAddonData)

def _do_displayError(
self,
onDisplayableError: "DisplayableError.OnDisplayableErrorT | None",
displayMessage: str,
titleMessage: str | None = _updateFailureMessage,
showTip: bool = True,
):
"""Display a DisplayableMessage if an OnDisplayableError action is given.

See gui.message.DisplayableError for further information.

:param onDisplayableError: The displayable error action.
:param displayMessage: Body of the displayable error.
:param titleMessage: Title of the displayable error, defaults to _updateFailureMessage.
:param showTip: Whether or not to show a suggestion of what to try, defaults to True.
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved
"""
if onDisplayableError is None:
return
from gui.message import DisplayableError

if showTip:
displayMessage += f'\n{self._updateFailureMirrorSuggestion if config.conf["addonStore"]["baseURL"] else self._updateFailureDefaultSuggestion}'
displayableError = DisplayableError(displayMessage, titleMessage)
callLater(delay=0, callable=onDisplayableError.notify, displayableError=displayableError)

SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved
def _deleteCacheInstalledAddon(self, addonId: str):
addonCachePath = os.path.join(self._installedAddonDataCacheDir, f"{addonId}.json")
if pathlib.Path(addonCachePath).exists():
Expand Down
16 changes: 13 additions & 3 deletions source/addonStore/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
)
import os
import pathlib
import posixpath
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved
import shutil
from typing import (
TYPE_CHECKING,
Expand All @@ -27,6 +28,7 @@
import NVDAState
from NVDAState import WritePaths
from utils.security import sha256_checksum
from config import conf

from .models.addon import (
_AddonGUIModel,
Expand All @@ -40,25 +42,33 @@
from gui.addonStoreGui.viewModels.addonList import AddonListItemVM


BASE_URL = "https://nvaccess.org/addonStore"
_DEFAULT_BASE_URL = "https://nvaccess.org/addonStore"
_LATEST_API_VER = "latest"
"""
A string value used in the add-on store to fetch the latest version of all add-ons,
i.e include older incompatible versions.
"""


def _getBaseURL() -> str:
if url := conf["addonStore"]["baseURL"]:
return url
return _DEFAULT_BASE_URL


def _getCurrentApiVersionForURL() -> str:
year, major, minor = addonAPIVersion.CURRENT
return f"{year}.{major}.{minor}"


def _getAddonStoreURL(channel: Channel, lang: str, nvdaApiVersion: str) -> str:
return f"{BASE_URL}/{lang}/{channel.value}/{nvdaApiVersion}.json"
# We don't know whether a user-supplied base url will have a trailing slash, so use posixpath.join, which inserts separators as needed.
return posixpath.join(_getBaseURL(), lang, channel.value, f"{nvdaApiVersion}.json")


def _getCacheHashURL() -> str:
return f"{BASE_URL}/cacheHash.json"
# We don't know whether a user-supplied base url will have a trailing slash, so use posixpath.join, which inserts separators as needed.
return posixpath.join(_getBaseURL(), "cacheHash.json")


class AddonFileDownloader:
Expand Down
1 change: 1 addition & 0 deletions source/config/configSpec.py
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@
[addonStore]
showWarning = boolean(default=true)
automaticUpdates = option("notify", "disabled", default="notify")
baseURL = string(default="")
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved
"""

#: The configuration specification
Expand Down
2 changes: 2 additions & 0 deletions source/config/validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def _validate_url(url: str) -> bool:
return True
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved
27 changes: 27 additions & 0 deletions source/gui/settingsDialogs.py
Original file line number Diff line number Diff line change
Expand Up @@ -3132,10 +3132,22 @@ def makeSettings(self, settingsSizer: wx.BoxSizer) -> None:
index = [x.value for x in AddonsAutomaticUpdate].index(config.conf["addonStore"]["automaticUpdates"])
self.automaticUpdatesComboBox.SetSelection(index)

# Translators: This is the label for a text box in the add-on store settings dialog.
addonMetadataMirrorLabel = _("Metadata &mirror")
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved
self.addonMetadataMirrorTextbox = sHelper.addLabeledControl(
addonMetadataMirrorLabel,
wx.TextCtrl,
)
self.addonMetadataMirrorTextbox.SetValue(config.conf["addonStore"]["baseURL"])
self.bindHelpEvent("AddonStoreMetadataMirror", self.addonMetadataMirrorTextbox)
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved
# self.addonUpdateMirrorTextbox.SetValidator(URLValidator)
seanbudd marked this conversation as resolved.
Show resolved Hide resolved
seanbudd marked this conversation as resolved.
Show resolved Hide resolved

def onSave(self):
index = self.automaticUpdatesComboBox.GetSelection()
config.conf["addonStore"]["automaticUpdates"] = [x.value for x in AddonsAutomaticUpdate][index]

config.conf["addonStore"]["baseURL"] = self.addonMetadataMirrorTextbox.Value.strip()
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved

seanbudd marked this conversation as resolved.
Show resolved Hide resolved

class TouchInteractionPanel(SettingsPanel):
# Translators: This is the label for the touch interaction settings panel.
Expand Down Expand Up @@ -5362,3 +5374,18 @@ def onFilterEditTextChange(self, evt):
self.filter(self.filterEdit.Value)
self._refreshVisibleItems()
evt.Skip()


# class URLValidator(wx.Validator):
# def Clone(self):
# return URLValidator()

# def Validate(self, parent):
# from urllib.parse import urlparse
# textControl = self.GetWindow()
# text = textControl.getValue()
# parsed = urlparse(text)
# isValid = all([parsed.scheme, parsed.netloc])
# if not isValid:
# wx.MessageBox("The mirror URL is invalid")
# return isValid
seanbudd marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 4 additions & 0 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

* When editing in Microsoft PowerPoint text boxes, you can now move per sentence with `alt+upArrow`/`alt+downArrow`. (#17015, @LeonarddeR)
* In Mozilla Firefox, NVDA will report the highlighted text when a URL containing a text fragment is visited. (#16910, @jcsteh)
* It is now possible to specify a mirror URL to use for the add-on store. (#14974)
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved

### Changes

Expand Down Expand Up @@ -35,6 +36,9 @@ Add-ons will need to be re-tested and have their manifest updated.
These are breaking API changes.
Please open a GitHub issue if your add-on has an issue with updating to the new API.

* The `addonStore.network.BASE_URL` constant has been removed.
As the add-on store base URL is now configurable directly within NVDA, no replacement is planned.
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved

#### Deprecations

* The `braille.filter_displaySize` extension point is deprecated.
Expand Down
7 changes: 7 additions & 0 deletions user_docs/en/userGuide.md
Original file line number Diff line number Diff line change
Expand Up @@ -3031,6 +3031,13 @@ For example, for installed beta add-ons, you will only be notified of updates wi
|Notify |Notify when updates are available to add-ons within the same channel |
|Disabled |Do not automatically check for updates to add-ons |

##### Metadata Mirror {#AddonStoreMetadataMirror}

This option allows you to specify an alternative URL to download add-on metadata from.
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved
This may be of use in locations where access to the NV Access Add-on Store server is slow.
SaschaCowley marked this conversation as resolved.
Show resolved Hide resolved

Leave this blank to use the default NV Access Add-on Store server.

#### Windows OCR Settings {#Win10OcrSettings}

The settings in this category allow you to configure [Windows OCR](#Win10Ocr).
Expand Down