Extensions: enable extensions.blender.org, adjust welcome screen

Splash screen notification text:

- When starting in "Offline mode" with repositories with installed
  packages enabled, the text is clickable unless launched with
  `--offline-mode`, the tooltip notes that the command line setting
  can't be changed at run-time.
- Don's show anything if there are not extensions installed.

Preferences:

- Don't show the welcome message on preferences if:
  - Blender is online.
  - The message was dismissed.
  - There are no enabled remote repositories.

- The option to enable extensions.blender.org has been replaced with
  a button that switches to the "System" tab where Online Access
  can be enabled.

Also expose bpy.app.online_access_override needed for the UI to check
if online access was disabled using command line arguments.
This commit is contained in:
Campbell Barton
2024-05-24 16:47:02 +10:00
parent 91cab8f992
commit 4f73df2b7b
7 changed files with 103 additions and 68 deletions

View File

@@ -149,7 +149,6 @@ def repos_to_notify():
# Since it's not all that common to disable the status bar just run notifications
# if any repositories are marked to run notifications.
online_access = bpy.app.online_access
prefs = bpy.context.preferences
extension_repos = prefs.extensions.repos
for repo_item in extension_repos:
@@ -164,14 +163,6 @@ def repos_to_notify():
if not remote_url:
continue
if online_access:
# All URL's may be accessed.
pass
else:
# Allow remote file-system repositories even when online access is disabled.
if not remote_url.startswith("file://"):
continue
# WARNING: this could be a more expensive check, use a "reasonable" guess.
# This is technically incorrect because knowing if a repository has any installed
# packages requires reading it's meta-data and comparing it with the directory contents.
@@ -195,6 +186,7 @@ def repos_to_notify():
if repo_is_empty:
continue
# NOTE: offline checks are handled by the notification (not here).
repos_notify.append(repo_item)
return repos_notify

View File

@@ -24,6 +24,9 @@ from . import bl_extension_utils
# only keep this as a reference and in case we can speed up forcing them to exit.
USE_GRACEFUL_EXIT = False
# Special value to signal no packages can be updated because all repositories are blocked by being offline.
STATE_DATA_ALL_OFFLINE = object()
# -----------------------------------------------------------------------------
# Internal Utilities
@@ -140,8 +143,19 @@ def sync_status_generator(repos_notify):
# Setup The Update #
# ################ #
repos_notify_orig = repos_notify
if not bpy.app.online_access:
repos_notify = [repo for repo in repos_notify if repo.remote_url.startswith("file://")]
if not repos_notify:
# Special case, early exit.
yield (STATE_DATA_ALL_OFFLINE, 0, ())
return
yield None
any_offline = len(repos_notify) != len(repos_notify_orig)
del repos_notify_orig
# An extension unique to this session.
unique_ext = "@{:x}".format(os.getpid())
@@ -244,10 +258,15 @@ def sync_status_generator(repos_notify):
# TODO: more elegant way to detect changes.
# Re-calculating the same information each time then checking if it's different isn't great.
if command_result.status_data_changed:
extra_warnings = []
if command_result.all_complete:
any_lock_errors = sync_apply_locked(repos_notify, repos_notify_files, unique_ext)
update_total = sync_status_count_outdated_extensions(repos_notify)
yield (cmd_batch.calc_status_data(), update_total, any_lock_errors)
if any_lock_errors:
extra_warnings.append(" Failed to acquire lock!")
if any_offline:
extra_warnings.append(" Skipping online repositories!")
yield (cmd_batch.calc_status_data(), update_total, extra_warnings)
else:
yield None
@@ -294,7 +313,7 @@ class NotifyHandle:
self.state = 0
# We could start the generator separately, this seems OK here for now.
self.sync_generator = iter(sync_status_generator(repos_notify))
# TEXT/ICON_ID/COUNT
# status_data, update_count, extra_warnings.
self.sync_info = None
@@ -354,11 +373,15 @@ def splash_draw_status_fn(self, context):
if _notify.sync_info is None:
self.layout.label(text="Updates starting...")
elif _notify.sync_info[0] is STATE_DATA_ALL_OFFLINE:
# The special case is ugly but showing this operator doesn't fit well with other kinds of status updates.
self.layout.operator("bl_pkg.extensions_show_online_prefs", text="Offline mode", icon='ORPHAN_DATA')
else:
status_data, update_count, any_lock_errors = _notify.sync_info
status_data, update_count, extra_warnings = _notify.sync_info
text, icon = bl_extension_utils.CommandBatch.calc_status_text_icon_from_data(status_data, update_count)
if any_lock_errors:
text = text + " - failed to acquire lock!"
# Not more than 1-2 of these (failed to lock, some repositories offline .. etc).
for warning in extra_warnings:
text = text + warning
row = self.layout.row(align=True)
if update_count > 0:
row.operator("bl_pkg.extensions_show_for_update", text=text, icon=icon)

View File

@@ -2245,42 +2245,27 @@ class BlPkgShowUpgrade(Operator):
return {'FINISHED'}
class BlPkgOnlineAccess(Operator):
"""Handle online access"""
bl_idname = "bl_pkg.extension_online_access"
# NOTE: this is a wrapper for `SCREEN_OT_userpref_show`.
# It exists *only* to add a poll function which sets a message when offline mode is forced.
class BlPkgShowOnlinePreference(Operator):
"""Show system preferences "Network" panel to allow online access"""
bl_idname = "bl_pkg.extensions_show_online_prefs"
bl_label = ""
bl_options = {'INTERNAL'}
enable: BoolProperty(
name="Enable",
default=False,
)
@classmethod
def poll(cls, context):
if bpy.app.online_access_override:
if not bpy.app.online_access:
cls.poll_message_set("Blender was launched in offline-mode which cannot be changed at runtime")
return False
return True
def execute(self, context):
wm = context.window_manager
prefs = context.preferences
remote_url = "https://extensions.blender.org/api/v1/extensions"
if self.enable:
extension_repos = prefs.extensions.repos
repo_found = None
for repo in extension_repos:
if repo.remote_url == remote_url:
repo_found = repo
break
if repo_found:
repo_found.enabled = True
else:
# While not expected, we want to know if this ever occurs, don't fail silently.
self.report({'WARNING'}, "Repository \"{:s}\" not found!".format(remote_url))
if bpy.app.online_access:
# Run the first check for updates automatically.
# Invoke the modal operator so users can cancel by pressing "Escape".
assert bpy.ops.bl_pkg.repo_sync_all.poll()
bpy.ops.bl_pkg.repo_sync_all('INVOKE_DEFAULT')
prefs.extensions.use_online_access_handled = True
bpy.ops.screen.userpref_show('INVOKE_DEFAULT', section='SYSTEM')
return {'FINISHED'}
@@ -2334,7 +2319,7 @@ classes = (
BlPkgRepoUnlock,
BlPkgShowUpgrade,
BlPkgOnlineAccess,
BlPkgShowOnlinePreference,
# Dummy, just shows a message.
BlPkgEnableNotInstalled,

View File

@@ -296,16 +296,21 @@ def extensions_panel_draw_online_extensions_request_impl(
if layout_panel is not None:
# Text wrapping isn't supported, manually wrap.
for line in (
"Welcome! Access community-made add-ons and themes from the",
"Welcome! Access community-made add-ons and themes from the ",
"extensions.blender.org repository.",
"",
"This also requires internet access which must be enabled in \"System\" preferences.",
"This requires online access which must be enabled in \"System\" preferences.",
):
layout_panel.label(text=line)
row = layout.row()
row.operator("bl_pkg.extension_online_access", text="Dismiss", icon='X').enable = False
row.operator("bl_pkg.extension_online_access", text="Enable Repository", icon='CHECKMARK').enable = True
props = row.operator("wm.context_set_boolean", text="Dismiss", icon='X')
props.data_path = "preferences.extensions.use_online_access_handled"
props.value = True
# The only reason to prefer this over `screen.userpref_show`
# is it will be disabled when `--offline-mode` is forced with a useful error for why.
row.operator("bl_pkg.extensions_show_online_prefs", text="Go to System")
def extensions_panel_draw_impl(
@@ -833,7 +838,16 @@ def extensions_panel_draw(panel, context):
if repo_status_text.running:
return
if not prefs.extensions.use_online_access_handled:
# Check if the extensions "Welcome" panel should be displayed.
# Even though it can be dismissed it's quite "in-your-face" so only show when it's needed.
if (
# The user didn't dismiss.
(not prefs.extensions.use_online_access_handled) and
# Running offline.
(not bpy.app.online_access) and
# There is one or more repositories that require remote access.
any(repo for repo in prefs.extensions.repos if repo.enabled and repo.use_remote_url)
):
extensions_panel_draw_online_extensions_request_impl(panel, context)
extensions_panel_draw_impl(

View File

@@ -204,8 +204,7 @@ bUserExtensionRepo *BKE_preferences_extension_repo_add_default(UserDef *userdef)
STRNCPY(repo->remote_url, "https://extensions.blender.org/api/v1/extensions");
/* Disable `blender.org` by default, the initial "Online Preferences" section gives
* the option to enable this. */
repo->flag |= USER_EXTENSION_REPO_FLAG_USE_REMOTE_URL | USER_EXTENSION_REPO_FLAG_DISABLED |
USER_EXTENSION_REPO_FLAG_SYNC_ON_STARTUP;
repo->flag |= USER_EXTENSION_REPO_FLAG_USE_REMOTE_URL | USER_EXTENSION_REPO_FLAG_SYNC_ON_STARTUP;
return repo;
}

View File

@@ -454,7 +454,34 @@ static void rna_userdef_script_autoexec_update(Main * /*bmain*/,
USERDEF_TAG_DIRTY;
}
int rna_userdef_use_online_access_editable(const PointerRNA * /*ptr*/, const char **r_info)
static void rna_userdef_use_online_access_set(PointerRNA *ptr, bool value)
{
/* A `set` function is needed to clear the override flags. */
UserDef *userdef = (UserDef *)ptr->data;
if ((G.f & G_FLAG_INTERNET_ALLOW) == 0) {
if (G.f & G_FLAG_INTERNET_OVERRIDE_PREF_OFFLINE) {
/* The `editable` check should account for this, assert since this is security related. */
BLI_assert_unreachable();
return;
}
}
if (value) {
userdef->flag |= USER_INTERNET_ALLOW;
G.f |= G_FLAG_INTERNET_ALLOW;
}
else {
userdef->flag &= ~USER_INTERNET_ALLOW;
G.f &= ~G_FLAG_INTERNET_ALLOW;
}
/* Once the user edits this option (even to set it to the value it was)
* it's no longer considered overridden. */
G.f &= ~G_FLAG_INTERNET_OVERRIDE_PREF_ANY;
}
static int rna_userdef_use_online_access_editable(const PointerRNA * /*ptr*/, const char **r_info)
{
if ((G.f & G_FLAG_INTERNET_ALLOW) == 0) {
/* Return 0 when blender was invoked with `--offline-mode` "forced". */
@@ -466,21 +493,6 @@ int rna_userdef_use_online_access_editable(const PointerRNA * /*ptr*/, const cha
return PROP_EDITABLE;
}
static void rna_userdef_use_online_access_update(Main * /*bmain*/,
Scene * /*scene*/,
PointerRNA *ptr)
{
UserDef *userdef = (UserDef *)ptr->data;
if (userdef->flag & USER_INTERNET_ALLOW) {
G.f |= G_FLAG_INTERNET_ALLOW;
}
else {
G.f &= ~G_FLAG_INTERNET_ALLOW;
}
USERDEF_TAG_DIRTY;
}
static void rna_userdef_script_directory_name_set(PointerRNA *ptr, const char *value)
{
bUserScriptDirectory *script_dir = static_cast<bUserScriptDirectory *>(ptr->data);
@@ -6175,13 +6187,14 @@ static void rna_def_userdef_system(BlenderRNA *brna)
prop = RNA_def_property(srna, "use_online_access", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "flag", USER_INTERNET_ALLOW);
RNA_def_property_boolean_funcs(prop, nullptr, "rna_userdef_use_online_access_set");
RNA_def_property_ui_text(
prop,
"Allow Online Access",
"Allow internet access. Blender may access configured online extension repositories. "
"Installed third party add-ons may access the internet for their own functionality");
RNA_def_property_editable_func(prop, "rna_userdef_use_online_access_editable");
RNA_def_property_update(prop, 0, "rna_userdef_use_online_access_update");
RNA_def_property_update(prop, 0, "rna_userdef_update");
/* Audio */

View File

@@ -257,6 +257,10 @@ PyDoc_STRVAR(
/* Wrap. */
bpy_app_internet_offline_doc,
"Boolean, true when internet access is allowed by Blender & 3rd party scripts (read-only)");
PyDoc_STRVAR(
/* Wrap. */
bpy_app_internet_offline_override_doc,
"Boolean, true when internet access preference is overridden by the command line (read-only)");
PyDoc_STRVAR(
/* Wrap. */
@@ -497,6 +501,11 @@ static PyGetSetDef bpy_app_getsets[] = {
nullptr,
bpy_app_internet_offline_doc,
(void *)G_FLAG_INTERNET_ALLOW},
{"online_access_override",
bpy_app_global_flag_get,
nullptr,
bpy_app_internet_offline_override_doc,
(void *)G_FLAG_INTERNET_OVERRIDE_PREF_ANY},
/* security */
{"autoexec_fail",