Check specific roles for specific addon features.
This commit is contained in:
parent
0f26551368
commit
d53938e03b
@ -52,6 +52,7 @@ class SyncStatusProperties(PropertyGroup):
|
|||||||
('INFO', 'INFO', ''),
|
('INFO', 'INFO', ''),
|
||||||
('WARNING', 'WARNING', ''),
|
('WARNING', 'WARNING', ''),
|
||||||
('ERROR', 'ERROR', ''),
|
('ERROR', 'ERROR', ''),
|
||||||
|
('SUBSCRIBE', 'SUBSCRIBE', ''),
|
||||||
],
|
],
|
||||||
name='level',
|
name='level',
|
||||||
update=redraw)
|
update=redraw)
|
||||||
@ -64,7 +65,11 @@ class SyncStatusProperties(PropertyGroup):
|
|||||||
# Message can also be empty, just to erase it from the GUI.
|
# Message can also be empty, just to erase it from the GUI.
|
||||||
# No need to actually log those.
|
# No need to actually log those.
|
||||||
if message:
|
if message:
|
||||||
log.log(logging._nameToLevel[self.level], message)
|
try:
|
||||||
|
loglevel = logging._nameToLevel[self.level]
|
||||||
|
except KeyError:
|
||||||
|
loglevel = logging.WARNING
|
||||||
|
log.log(loglevel, message)
|
||||||
|
|
||||||
# List of syncable versions is stored in 'available_blender_versions' ID property,
|
# List of syncable versions is stored in 'available_blender_versions' ID property,
|
||||||
# because I don't know how to store a variable list of strings in a proper RNA property.
|
# because I don't know how to store a variable list of strings in a proper RNA property.
|
||||||
@ -157,15 +162,25 @@ class BlenderCloudPreferences(AddonPreferences):
|
|||||||
'INFO': 'NONE',
|
'INFO': 'NONE',
|
||||||
'WARNING': 'INFO',
|
'WARNING': 'INFO',
|
||||||
'ERROR': 'ERROR',
|
'ERROR': 'ERROR',
|
||||||
|
'SUBSCRIBE': 'ERROR',
|
||||||
}
|
}
|
||||||
message_container = row.row()
|
message_container = row.row()
|
||||||
message_container.label(bss.message, icon=icon_for_level[bss.level])
|
message_container.label(bss.message, icon=icon_for_level[bss.level])
|
||||||
message_container.alert = True # bss.level in {'WARNING', 'ERROR'}
|
|
||||||
|
|
||||||
sub = bsync_box.column()
|
sub = bsync_box.column()
|
||||||
sub.enabled = bss.status in {'NONE', 'IDLE'}
|
|
||||||
|
|
||||||
buttons = sub.column()
|
if bss.level == 'SUBSCRIBE':
|
||||||
|
self.draw_subscribe_button(sub)
|
||||||
|
else:
|
||||||
|
self.draw_sync_buttons(sub, bss)
|
||||||
|
|
||||||
|
def draw_subscribe_button(self, layout):
|
||||||
|
layout.operator('pillar.subscribe', icon='WORLD')
|
||||||
|
|
||||||
|
def draw_sync_buttons(self, layout, bss):
|
||||||
|
layout.enabled = bss.status in {'NONE', 'IDLE'}
|
||||||
|
|
||||||
|
buttons = layout.column()
|
||||||
row_buttons = buttons.row().split(percentage=0.5)
|
row_buttons = buttons.row().split(percentage=0.5)
|
||||||
row_pull = row_buttons.row(align=True)
|
row_pull = row_buttons.row(align=True)
|
||||||
row_push = row_buttons.row()
|
row_push = row_buttons.row()
|
||||||
@ -194,7 +209,8 @@ class BlenderCloudPreferences(AddonPreferences):
|
|||||||
row_pull.label('Cloud Sync is running.')
|
row_pull.label('Cloud Sync is running.')
|
||||||
|
|
||||||
|
|
||||||
class PillarCredentialsUpdate(Operator):
|
class PillarCredentialsUpdate(pillar.PillarOperatorMixin,
|
||||||
|
Operator):
|
||||||
"""Updates the Pillar URL and tests the new URL."""
|
"""Updates the Pillar URL and tests the new URL."""
|
||||||
bl_idname = 'pillar.credentials_update'
|
bl_idname = 'pillar.credentials_update'
|
||||||
bl_label = 'Update credentials'
|
bl_label = 'Update credentials'
|
||||||
@ -224,7 +240,7 @@ class PillarCredentialsUpdate(Operator):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
loop.run_until_complete(pillar.refresh_pillar_credentials())
|
loop.run_until_complete(self.check_credentials(context, set()))
|
||||||
except blender_id.BlenderIdCommError as ex:
|
except blender_id.BlenderIdCommError as ex:
|
||||||
log.exception('Error sending subclient-specific token to Blender ID')
|
log.exception('Error sending subclient-specific token to Blender ID')
|
||||||
self.report({'ERROR'}, 'Failed to sync Blender ID to Blender Cloud')
|
self.report({'ERROR'}, 'Failed to sync Blender ID to Blender Cloud')
|
||||||
@ -238,6 +254,20 @@ class PillarCredentialsUpdate(Operator):
|
|||||||
return {'FINISHED'}
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
|
class PILLAR_OT_subscribe(Operator):
|
||||||
|
"""Opens a browser to subscribe the user to the Cloud."""
|
||||||
|
bl_idname = 'pillar.subscribe'
|
||||||
|
bl_label = 'Subscribe to the Cloud'
|
||||||
|
|
||||||
|
def execute(self, context):
|
||||||
|
import webbrowser
|
||||||
|
|
||||||
|
webbrowser.open_new_tab('https://cloud.blender.org/join')
|
||||||
|
self.report({'INFO'}, 'We just started a browser for you.')
|
||||||
|
|
||||||
|
return {'FINISHED'}
|
||||||
|
|
||||||
|
|
||||||
def preferences() -> BlenderCloudPreferences:
|
def preferences() -> BlenderCloudPreferences:
|
||||||
return bpy.context.user_preferences.addons[ADDON_NAME].preferences
|
return bpy.context.user_preferences.addons[ADDON_NAME].preferences
|
||||||
|
|
||||||
@ -246,6 +276,7 @@ def register():
|
|||||||
bpy.utils.register_class(BlenderCloudPreferences)
|
bpy.utils.register_class(BlenderCloudPreferences)
|
||||||
bpy.utils.register_class(PillarCredentialsUpdate)
|
bpy.utils.register_class(PillarCredentialsUpdate)
|
||||||
bpy.utils.register_class(SyncStatusProperties)
|
bpy.utils.register_class(SyncStatusProperties)
|
||||||
|
bpy.utils.register_class(PILLAR_OT_subscribe)
|
||||||
|
|
||||||
addon_prefs = preferences()
|
addon_prefs = preferences()
|
||||||
|
|
||||||
@ -274,6 +305,7 @@ def unregister():
|
|||||||
bpy.utils.unregister_class(PillarCredentialsUpdate)
|
bpy.utils.unregister_class(PillarCredentialsUpdate)
|
||||||
bpy.utils.unregister_class(BlenderCloudPreferences)
|
bpy.utils.unregister_class(BlenderCloudPreferences)
|
||||||
bpy.utils.unregister_class(SyncStatusProperties)
|
bpy.utils.unregister_class(SyncStatusProperties)
|
||||||
|
bpy.utils.unregister_class(PILLAR_OT_subscribe)
|
||||||
|
|
||||||
del WindowManager.last_blender_cloud_location
|
del WindowManager.last_blender_cloud_location
|
||||||
del WindowManager.blender_sync_status
|
del WindowManager.blender_sync_status
|
||||||
|
@ -30,6 +30,8 @@ import os
|
|||||||
import pillarsdk
|
import pillarsdk
|
||||||
from . import async_loop, pillar, cache
|
from . import async_loop, pillar, cache
|
||||||
|
|
||||||
|
REQUIRED_ROLES_FOR_TEXTURE_BROWSER = {'subscriber', 'demo'}
|
||||||
|
|
||||||
icon_width = 128
|
icon_width = 128
|
||||||
icon_height = 128
|
icon_height = 128
|
||||||
target_item_width = 400
|
target_item_width = 400
|
||||||
@ -320,7 +322,7 @@ class BlenderCloudBrowser(pillar.PillarOperatorMixin,
|
|||||||
self.log.debug('Checking credentials')
|
self.log.debug('Checking credentials')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = await self.check_credentials(context)
|
user_id = await self.check_credentials(context, REQUIRED_ROLES_FOR_TEXTURE_BROWSER)
|
||||||
except pillar.NotSubscribedToCloudError:
|
except pillar.NotSubscribedToCloudError:
|
||||||
self.log.info('User not subscribed to Blender Cloud.')
|
self.log.info('User not subscribed to Blender Cloud.')
|
||||||
self._show_subscribe_screen()
|
self._show_subscribe_screen()
|
||||||
|
@ -177,9 +177,10 @@ async def pillar_call(pillar_func, *args, caching=True, **kwargs):
|
|||||||
return await loop.run_in_executor(None, partial)
|
return await loop.run_in_executor(None, partial)
|
||||||
|
|
||||||
|
|
||||||
async def check_pillar_credentials():
|
async def check_pillar_credentials(required_roles: set):
|
||||||
"""Tries to obtain the user at Pillar using the user's credentials.
|
"""Tries to obtain the user at Pillar using the user's credentials.
|
||||||
|
|
||||||
|
:param required_roles: set of roles to require -- having one of those is enough.
|
||||||
:raises UserNotLoggedInError: when the user is not logged in on Blender ID.
|
:raises UserNotLoggedInError: when the user is not logged in on Blender ID.
|
||||||
:raises CredentialsNotSyncedError: when the user is logged in on Blender ID but
|
:raises CredentialsNotSyncedError: when the user is logged in on Blender ID but
|
||||||
doesn't have a valid subclient token for Pillar.
|
doesn't have a valid subclient token for Pillar.
|
||||||
@ -203,9 +204,9 @@ async def check_pillar_credentials():
|
|||||||
except (pillarsdk.UnauthorizedAccess, pillarsdk.ResourceNotFound, pillarsdk.ForbiddenAccess):
|
except (pillarsdk.UnauthorizedAccess, pillarsdk.ResourceNotFound, pillarsdk.ForbiddenAccess):
|
||||||
raise CredentialsNotSyncedError()
|
raise CredentialsNotSyncedError()
|
||||||
|
|
||||||
roles = db_user.roles
|
roles = db_user.roles or set()
|
||||||
log.debug('User has roles %r', roles)
|
log.debug('User has roles %r', roles)
|
||||||
if not roles or not {'subscriber', 'demo'}.intersection(set(roles)):
|
if required_roles and not required_roles.intersection(set(roles)):
|
||||||
# Delete the subclient info. This forces a re-check later, which can
|
# Delete the subclient info. This forces a re-check later, which can
|
||||||
# then pick up on the user's new status.
|
# then pick up on the user's new status.
|
||||||
del profile.subclients[SUBCLIENT_ID]
|
del profile.subclients[SUBCLIENT_ID]
|
||||||
@ -215,7 +216,7 @@ async def check_pillar_credentials():
|
|||||||
return pillar_user_id
|
return pillar_user_id
|
||||||
|
|
||||||
|
|
||||||
async def refresh_pillar_credentials():
|
async def refresh_pillar_credentials(required_roles: set):
|
||||||
"""Refreshes the authentication token on Pillar.
|
"""Refreshes the authentication token on Pillar.
|
||||||
|
|
||||||
:raises blender_id.BlenderIdCommError: when Blender ID refuses to send a token to Pillar.
|
:raises blender_id.BlenderIdCommError: when Blender ID refuses to send a token to Pillar.
|
||||||
@ -239,7 +240,7 @@ async def refresh_pillar_credentials():
|
|||||||
|
|
||||||
# Test the new URL
|
# Test the new URL
|
||||||
_pillar_api = None
|
_pillar_api = None
|
||||||
return await check_pillar_credentials()
|
return await check_pillar_credentials(required_roles)
|
||||||
|
|
||||||
|
|
||||||
async def get_project_uuid(project_url: str) -> str:
|
async def get_project_uuid(project_url: str) -> str:
|
||||||
@ -662,8 +663,7 @@ def is_cancelled(future: asyncio.Future) -> bool:
|
|||||||
|
|
||||||
|
|
||||||
class PillarOperatorMixin:
|
class PillarOperatorMixin:
|
||||||
|
async def check_credentials(self, context, required_roles) -> bool:
|
||||||
async def check_credentials(self, context) -> bool:
|
|
||||||
"""Checks credentials with Pillar, and if ok returns the user ID.
|
"""Checks credentials with Pillar, and if ok returns the user ID.
|
||||||
|
|
||||||
Returns None if the user cannot be found, or if the user is not a Cloud subscriber.
|
Returns None if the user cannot be found, or if the user is not a Cloud subscriber.
|
||||||
@ -672,7 +672,7 @@ class PillarOperatorMixin:
|
|||||||
# self.report({'INFO'}, 'Checking Blender Cloud credentials')
|
# self.report({'INFO'}, 'Checking Blender Cloud credentials')
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = await check_pillar_credentials()
|
user_id = await check_pillar_credentials(required_roles)
|
||||||
except NotSubscribedToCloudError:
|
except NotSubscribedToCloudError:
|
||||||
self._log_subscription_needed()
|
self._log_subscription_needed()
|
||||||
raise
|
raise
|
||||||
@ -683,7 +683,7 @@ class PillarOperatorMixin:
|
|||||||
return user_id
|
return user_id
|
||||||
|
|
||||||
try:
|
try:
|
||||||
user_id = await refresh_pillar_credentials()
|
user_id = await refresh_pillar_credentials(required_roles)
|
||||||
except NotSubscribedToCloudError:
|
except NotSubscribedToCloudError:
|
||||||
self._log_subscription_needed()
|
self._log_subscription_needed()
|
||||||
raise
|
raise
|
||||||
|
@ -30,6 +30,7 @@ LOCAL_SETTINGS_RNA = [
|
|||||||
(b'tempdir', 'filepaths.temporary_directory'),
|
(b'tempdir', 'filepaths.temporary_directory'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
REQUIRES_ROLES_FOR_SYNC = {'subscriber', 'demo'}
|
||||||
HOME_PROJECT_ENDPOINT = '/bcloud/home-project'
|
HOME_PROJECT_ENDPOINT = '/bcloud/home-project'
|
||||||
SYNC_GROUP_NODE_NAME = 'Blender Sync'
|
SYNC_GROUP_NODE_NAME = 'Blender Sync'
|
||||||
SYNC_GROUP_NODE_DESC = 'The [Blender Cloud Addon](https://cloud.blender.org/services' \
|
SYNC_GROUP_NODE_DESC = 'The [Blender Cloud Addon](https://cloud.blender.org/services' \
|
||||||
@ -354,9 +355,22 @@ class PILLAR_OT_sync(pillar.PillarOperatorMixin,
|
|||||||
self.log.info('Performing action %s', action)
|
self.log.info('Performing action %s', action)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.user_id = await self.check_credentials(context)
|
# Refresh credentials
|
||||||
|
try:
|
||||||
|
self.user_id = await self.check_credentials(context, REQUIRES_ROLES_FOR_SYNC)
|
||||||
log.debug('Found user ID: %s', self.user_id)
|
log.debug('Found user ID: %s', self.user_id)
|
||||||
|
except pillar.NotSubscribedToCloudError:
|
||||||
|
self.log.exception('User not subscribed to cloud.')
|
||||||
|
self.bss_report({'SUBSCRIBE'}, 'Please subscribe to the Blender Cloud.')
|
||||||
|
self._state = 'QUIT'
|
||||||
|
return
|
||||||
|
except pillar.CredentialsNotSyncedError:
|
||||||
|
self.log.exception('Error checking/refreshing credentials.')
|
||||||
|
self.bss_report({'ERROR'}, 'Please log in on Blender ID first.')
|
||||||
|
self._state = 'QUIT'
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find the home project.
|
||||||
try:
|
try:
|
||||||
self.home_project_id = await get_home_project_id()
|
self.home_project_id = await get_home_project_id()
|
||||||
except sdk_exceptions.ForbiddenAccess:
|
except sdk_exceptions.ForbiddenAccess:
|
||||||
@ -390,9 +404,6 @@ class PILLAR_OT_sync(pillar.PillarOperatorMixin,
|
|||||||
'REFRESH': self.action_refresh,
|
'REFRESH': self.action_refresh,
|
||||||
}[action]
|
}[action]
|
||||||
await action_method(context)
|
await action_method(context)
|
||||||
except pillar.CredentialsNotSyncedError:
|
|
||||||
self.log.exception('Error checking/refreshing credentials.')
|
|
||||||
self.bss_report({'ERROR'}, 'Please log in on Blender ID first.')
|
|
||||||
except Exception as ex:
|
except Exception as ex:
|
||||||
self.log.exception('Unexpected exception caught.')
|
self.log.exception('Unexpected exception caught.')
|
||||||
self.bss_report({'ERROR'}, 'Unexpected error: %s' % ex)
|
self.bss_report({'ERROR'}, 'Unexpected error: %s' % ex)
|
||||||
|
Reference in New Issue
Block a user