diff --git a/scripts/startup/bl_ui/space_userpref.py b/scripts/startup/bl_ui/space_userpref.py index 380f6a625b5..e8a30887c46 100644 --- a/scripts/startup/bl_ui/space_userpref.py +++ b/scripts/startup/bl_ui/space_userpref.py @@ -2137,24 +2137,41 @@ class USERPREF_PT_extensions_repos(Panel): split.prop(active_repo, "remote_url", text="", icon='URL', placeholder="Repository URL") split = row.split() + if active_repo.use_access_token: + access_token_icon = 'LOCKED' if active_repo.access_token else 'UNLOCKED' + row = layout.row() + split = row.split(factor=0.936) + split.prop(active_repo, "access_token", icon=access_token_icon) + split = row.split() + layout.prop(active_repo, "use_sync_on_startup") layout_header, layout_panel = layout.panel("advanced", default_closed=True) layout_header.label(text="Advanced") - if layout_panel: - layout_panel.prop(active_repo, "use_custom_directory") - row = layout_panel.row() + if layout_panel: + layout_panel.use_property_split = True + + col = layout_panel.column(align=False, heading="Custom Directory") + row = col.row(align=True) + sub = row.row(align=True) + sub.prop(active_repo, "use_custom_directory", text="") + sub = sub.row(align=True) + sub.active = active_repo.use_custom_directory if active_repo.use_custom_directory: if active_repo.custom_directory == "": - row.alert = True - row.prop(active_repo, "custom_directory", text="") + sub.alert = True + sub.prop(active_repo, "custom_directory", text="") else: # Show the read-only directory property. # Apart from being consistent with the custom directory UI, # prefer a read-only property over a label because this is not necessarily # valid UTF-8 which will raise a Python exception when passed in as text. - row.prop(active_repo, "directory", text="") + sub.prop(active_repo, "directory", text="") + + if active_repo.use_remote_url: + row = layout_panel.row(align=True, heading="Authentication") + row.prop(active_repo, "use_access_token") layout_panel.prop(active_repo, "use_cache") layout_panel.separator() diff --git a/source/blender/blenkernel/BKE_preferences.h b/source/blender/blenkernel/BKE_preferences.h index b2241e9bfe2..2c736ba8dc7 100644 --- a/source/blender/blenkernel/BKE_preferences.h +++ b/source/blender/blenkernel/BKE_preferences.h @@ -15,6 +15,8 @@ extern "C" { #include "BLI_compiler_attrs.h" #include "BLI_sys_types.h" +struct BlendWriter; +struct BlendReader; struct UserDef; struct bUserExtensionRepo; struct bUserAssetLibrary; @@ -121,6 +123,11 @@ void BKE_preferences_extension_remote_to_name(const char *remote_url, char name[ int BKE_preferences_extension_repo_get_index(const UserDef *userdef, const bUserExtensionRepo *repo); +void BKE_preferences_extension_repo_read_data(struct BlendDataReader *reader, + bUserExtensionRepo *repo); +void BKE_preferences_extension_repo_write_data(struct BlendWriter *writer, + const bUserExtensionRepo *repo); + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index 482e8a6beb8..930f7fd0120 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -340,7 +340,13 @@ void BKE_blender_userdef_data_free(UserDef *userdef, bool clear_fonts) BLI_freelistN(&userdef->autoexec_paths); BLI_freelistN(&userdef->script_directories); BLI_freelistN(&userdef->asset_libraries); - BLI_freelistN(&userdef->extension_repos); + + LISTBASE_FOREACH_MUTABLE (bUserExtensionRepo *, repo_ref, &userdef->extension_repos) { + MEM_SAFE_FREE(repo_ref->access_token); + MEM_freeN(repo_ref); + } + BLI_listbase_clear(&userdef->asset_shelves_settings); + LISTBASE_FOREACH_MUTABLE (bUserAssetShelfSettings *, settings, &userdef->asset_shelves_settings) { BKE_asset_catalog_path_list_free(settings->enabled_catalog_paths); diff --git a/source/blender/blenkernel/intern/preferences.cc b/source/blender/blenkernel/intern/preferences.cc index a715929d757..879802bc608 100644 --- a/source/blender/blenkernel/intern/preferences.cc +++ b/source/blender/blenkernel/intern/preferences.cc @@ -25,6 +25,8 @@ #include "BLT_translation.hh" +#include "BLO_read_write.hh" + #include "DNA_asset_types.h" #include "DNA_defaults.h" #include "DNA_userdef_types.h" @@ -404,6 +406,21 @@ int BKE_preferences_extension_repo_get_index(const UserDef *userdef, { return BLI_findindex(&userdef->extension_repos, repo); } + +void BKE_preferences_extension_repo_read_data(BlendDataReader *reader, bUserExtensionRepo *repo) +{ + if (repo->access_token) { + BLO_read_string(reader, &repo->access_token); + } +} + +void BKE_preferences_extension_repo_write_data(BlendWriter *writer, const bUserExtensionRepo *repo) +{ + if (repo->access_token) { + BLO_write_string(writer, repo->access_token); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc index acb0b83ccc6..89ab186f392 100644 --- a/source/blender/blenloader/intern/readfile.cc +++ b/source/blender/blenloader/intern/readfile.cc @@ -82,6 +82,7 @@ #include "BKE_node.hh" /* for tree type defines */ #include "BKE_object.hh" #include "BKE_packedFile.h" +#include "BKE_preferences.h" #include "BKE_report.hh" #include "BKE_scene.hh" #include "BKE_screen.hh" @@ -3424,6 +3425,10 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) IDP_BlendDataRead(reader, &addon->prop); } + LISTBASE_FOREACH (bUserExtensionRepo *, repo_ref, &user->extension_repos) { + BKE_preferences_extension_repo_read_data(reader, repo_ref); + } + LISTBASE_FOREACH (bUserAssetShelfSettings *, shelf_settings, &user->asset_shelves_settings) { BKE_asset_catalog_path_list_blend_read_data(reader, shelf_settings->enabled_catalog_paths); } diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index bfa31405a0a..4d53e2d3553 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -114,6 +114,7 @@ #include "BKE_main_namemap.hh" #include "BKE_node.hh" #include "BKE_packedFile.h" +#include "BKE_preferences.h" #include "BKE_report.hh" #include "BKE_workspace.hh" @@ -934,6 +935,7 @@ static void write_userdef(BlendWriter *writer, const UserDef *userdef) LISTBASE_FOREACH (const bUserExtensionRepo *, repo_ref, &userdef->extension_repos) { BLO_write_struct(writer, bUserExtensionRepo, repo_ref); + BKE_preferences_extension_repo_write_data(writer, repo_ref); } LISTBASE_FOREACH ( const bUserAssetShelfSettings *, shelf_settings, &userdef->asset_shelves_settings) diff --git a/source/blender/editors/space_userpref/userpref_ops.cc b/source/blender/editors/space_userpref/userpref_ops.cc index 026fcd14c29..1c81614436a 100644 --- a/source/blender/editors/space_userpref/userpref_ops.cc +++ b/source/blender/editors/space_userpref/userpref_ops.cc @@ -275,9 +275,11 @@ static int preferences_extension_repo_add_exec(bContext *C, wmOperator *op) char name[sizeof(bUserExtensionRepo::name)] = ""; char remote_url[sizeof(bUserExtensionRepo::remote_url)] = ""; + char *access_token = nullptr; char custom_directory[sizeof(bUserExtensionRepo::custom_dirpath)] = ""; const bool use_custom_directory = RNA_boolean_get(op->ptr, "use_custom_directory"); + const bool use_access_token = RNA_boolean_get(op->ptr, "use_access_token"); const bool use_sync_on_startup = RNA_boolean_get(op->ptr, "use_sync_on_startup"); if (use_custom_directory) { RNA_string_get(op->ptr, "custom_directory", custom_directory); @@ -286,6 +288,12 @@ static int preferences_extension_repo_add_exec(bContext *C, wmOperator *op) if (repo_type == bUserExtensionRepoAddType::Remote) { RNA_string_get(op->ptr, "remote_url", remote_url); + + if (use_access_token) { + if (RNA_string_length(op->ptr, "access_token")) { + access_token = RNA_string_get_alloc(op->ptr, "access_token", nullptr, 0, nullptr); + } + } } /* Setup the name using the following logic: @@ -353,6 +361,13 @@ static int preferences_extension_repo_add_exec(bContext *C, wmOperator *op) if (repo_type == bUserExtensionRepoAddType::Remote) { STRNCPY(new_repo->remote_url, remote_url); new_repo->flag |= USER_EXTENSION_REPO_FLAG_USE_REMOTE_URL; + + if (use_access_token) { + new_repo->flag |= USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN; + } + if (access_token) { + new_repo->access_token = access_token; + } } /* Activate new repository in the UI for further setup. */ @@ -401,6 +416,23 @@ static void preferences_extension_repo_add_ui(bContext * /*C*/, wmOperator *op) case bUserExtensionRepoAddType::Remote: { uiItemR(layout, op->ptr, "remote_url", UI_ITEM_R_IMMEDIATE, nullptr, ICON_NONE); uiItemR(layout, op->ptr, "use_sync_on_startup", UI_ITEM_NONE, nullptr, ICON_NONE); + + uiItemS_ex(layout, 0.2f, LayoutSeparatorType::Line); + + const bool use_access_token = RNA_boolean_get(ptr, "use_access_token"); + const int token_icon = (use_access_token && RNA_string_length(op->ptr, "access_token")) ? + ICON_LOCKED : + ICON_UNLOCKED; + + uiLayout *row = uiLayoutRowWithHeading(layout, true, IFACE_("Authentication")); + uiItemR(row, op->ptr, "use_access_token", UI_ITEM_NONE, nullptr, ICON_NONE); + uiLayout *col = uiLayoutRow(layout, false); + uiLayoutSetActive(col, use_access_token); + /* Use "immediate" flag to refresh the icon. */ + uiItemR(col, op->ptr, "access_token", UI_ITEM_R_IMMEDIATE, nullptr, token_icon); + + uiItemS_ex(layout, 0.2f, LayoutSeparatorType::Line); + break; } case bUserExtensionRepoAddType::Local: { @@ -479,6 +511,30 @@ static void PREFERENCES_OT_extension_repo_add(wmOperatorType *ot) RNA_def_property_flag(prop, PROP_SKIP_SAVE); } + { /* Use Access Token. */ + const char *prop_id = "use_access_token"; + PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id); + PropertyRNA *prop = RNA_def_boolean(ot->srna, + prop_id, + false, + RNA_property_ui_name_raw(prop_ref), + RNA_property_ui_description_raw(prop_ref)); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + } + + { /* Access Token (dynamic length). */ + const char *prop_id = "access_token"; + PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id); + PropertyRNA *prop = RNA_def_string(ot->srna, + prop_id, + nullptr, + 0, + RNA_property_ui_name_raw(prop_ref), + RNA_property_ui_description_raw(prop_ref)); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_property_subtype(prop, PROP_PASSWORD); + } + { /* Check for Updated on Startup. */ const char *prop_id = "use_sync_on_startup"; PropertyRNA *prop_ref = RNA_struct_type_find_property(type_ref, prop_id); diff --git a/source/blender/makesdna/DNA_userdef_types.h b/source/blender/makesdna/DNA_userdef_types.h index 05d5a2e2751..9d9d7c6393a 100644 --- a/source/blender/makesdna/DNA_userdef_types.h +++ b/source/blender/makesdna/DNA_userdef_types.h @@ -636,6 +636,12 @@ typedef struct bUserExtensionRepo { */ char module[48]; + /** + * Secret access token for remote repositories (allocated). + * Only use when #USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN is set. + */ + char *access_token; + /** * The "local" directory where extensions are stored. * When unset, use `{BLENDER_USER_EXTENSIONS}/{bUserExtensionRepo::module}`. @@ -654,6 +660,7 @@ typedef enum eUserExtensionRepo_Flag { USER_EXTENSION_REPO_FLAG_USE_CUSTOM_DIRECTORY = 1 << 2, USER_EXTENSION_REPO_FLAG_USE_REMOTE_URL = 1 << 3, USER_EXTENSION_REPO_FLAG_SYNC_ON_STARTUP = 1 << 4, + USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN = 1 << 5, } eUserExtensionRepo_Flag; typedef struct SolidLight { diff --git a/source/blender/makesrna/intern/rna_userdef.cc b/source/blender/makesrna/intern/rna_userdef.cc index de50daaa4d9..29ad3c4ff97 100644 --- a/source/blender/makesrna/intern/rna_userdef.cc +++ b/source/blender/makesrna/intern/rna_userdef.cc @@ -385,6 +385,32 @@ static int rna_userdef_extension_repo_directory_length(PointerRNA *ptr) return BKE_preferences_extension_repo_dirpath_get(repo, dirpath, sizeof(dirpath)); } +static void rna_userdef_extension_repo_access_token_get(PointerRNA *ptr, char *value) +{ + const bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data; + if (repo->access_token) { + strcpy(value, repo->access_token); + } + else { + value[0] = '\0'; + } +} + +static int rna_userdef_extension_repo_access_token_length(PointerRNA *ptr) +{ + const bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data; + return (repo->access_token) ? strlen(repo->access_token) : 0; +} + +static void rna_userdef_extension_repo_access_token_set(PointerRNA *ptr, const char *value) +{ + bUserExtensionRepo *repo = (bUserExtensionRepo *)ptr->data; + if (repo->access_token) { + MEM_freeN(repo->access_token); + } + repo->access_token = value[0] ? BLI_strdup(value) : nullptr; +} + static void rna_userdef_extension_repo_generic_flag_set_impl(PointerRNA *ptr, const bool value, const int flag) @@ -6693,6 +6719,14 @@ static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna) RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_EDITOR_FILEBROWSER); RNA_def_property_update(prop, 0, "rna_userdef_update"); + prop = RNA_def_property(srna, "access_token", PROP_STRING, PROP_PASSWORD); + RNA_def_property_ui_text( + prop, "Secret", "Personal access token, may be required by some repositories"); + RNA_def_property_string_funcs(prop, + "rna_userdef_extension_repo_access_token_get", + "rna_userdef_extension_repo_access_token_length", + "rna_userdef_extension_repo_access_token_set"); + /* NOTE(@ideasman42): this is intended to be used by a package manger component * which is not yet integrated. */ prop = RNA_def_property(srna, "use_cache", PROP_BOOLEAN, PROP_NONE); @@ -6711,6 +6745,10 @@ static void rna_def_userdef_filepaths_extension_repo(BlenderRNA *brna) RNA_def_property_ui_text( prop, "Check for Updates on Startup", "Allow Blender to check for updates upon launch"); + prop = RNA_def_property(srna, "use_access_token", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", USER_EXTENSION_REPO_FLAG_USE_ACCESS_TOKEN); + RNA_def_property_ui_text(prop, "Requires Access Token", "Repository requires an access token"); + prop = RNA_def_property(srna, "use_custom_directory", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna( prop, nullptr, "flag", USER_EXTENSION_REPO_FLAG_USE_CUSTOM_DIRECTORY);