Merge pull request #1632 from pypeclub/feature/admin_and_project_manager

This commit is contained in:
Milan Kolar 2021-06-03 16:23:24 +02:00 committed by GitHub
commit ac43d8c254
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 227 additions and 52 deletions

View file

@ -97,7 +97,8 @@ from .local_settings import (
OpenPypeSettingsRegistry,
get_local_site_id,
change_openpype_mongo_url,
get_openpype_username
get_openpype_username,
is_admin_password_required
)
from .applications import (
@ -209,6 +210,7 @@ __all__ = [
"get_local_site_id",
"change_openpype_mongo_url",
"get_openpype_username",
"is_admin_password_required",
"ApplicationLaunchFailed",
"ApplictionExecutableNotFound",

View file

@ -29,7 +29,10 @@ except ImportError:
import six
import appdirs
from openpype.settings import get_local_settings
from openpype.settings import (
get_local_settings,
get_system_settings
)
from .import validate_mongo_connection
@ -562,3 +565,16 @@ def get_openpype_username():
if not username:
username = getpass.getuser()
return username
def is_admin_password_required():
system_settings = get_system_settings()
password = system_settings["general"].get("admin_password")
if not password:
return False
local_settings = get_local_settings()
is_admin = local_settings.get("general", {}).get("is_admin", False)
if is_admin:
return False
return True

View file

@ -36,6 +36,7 @@ from .clockify import ClockifyModule
from .log_viewer import LogViewModule
from .muster import MusterModule
from .deadline import DeadlineModule
from .project_manager_action import ProjectManagerAction
from .standalonepublish_action import StandAlonePublishAction
from .sync_server import SyncServerModule
@ -73,6 +74,7 @@ __all__ = (
"LogViewModule",
"MusterModule",
"DeadlineModule",
"ProjectManagerAction",
"StandAlonePublishAction",
"SyncServerModule"

View file

@ -86,7 +86,7 @@ class AvalonModule(PypeModule, ITrayModule, IWebServerRoutes):
from Qt import QtWidgets
# Actions
action_library_loader = QtWidgets.QAction(
"Library loader", tray_menu
"Loader", tray_menu
)
action_library_loader.triggered.connect(self.show_library_loader)

View file

@ -184,6 +184,9 @@ class ITrayAction(ITrayModule):
necessary.
"""
admin_action = False
_admin_submenu = None
@property
@abstractmethod
def label(self):
@ -197,9 +200,19 @@ class ITrayAction(ITrayModule):
def tray_menu(self, tray_menu):
from Qt import QtWidgets
action = QtWidgets.QAction(self.label, tray_menu)
if self.admin_action:
menu = self.admin_submenu(tray_menu)
action = QtWidgets.QAction(self.label, menu)
menu.addAction(action)
if not menu.menuAction().isVisible():
menu.menuAction().setVisible(True)
else:
action = QtWidgets.QAction(self.label, tray_menu)
tray_menu.addAction(action)
action.triggered.connect(self.on_action_trigger)
tray_menu.addAction(action)
def tray_start(self):
return
@ -207,6 +220,16 @@ class ITrayAction(ITrayModule):
def tray_exit(self):
return
@staticmethod
def admin_submenu(tray_menu):
if ITrayAction._admin_submenu is None:
from Qt import QtWidgets
admin_submenu = QtWidgets.QMenu("Admin", tray_menu)
admin_submenu.menuAction().setVisible(False)
ITrayAction._admin_submenu = admin_submenu
return ITrayAction._admin_submenu
class ITrayService(ITrayModule):
# Module's property
@ -233,6 +256,7 @@ class ITrayService(ITrayModule):
def services_submenu(tray_menu):
if ITrayService._services_submenu is None:
from Qt import QtWidgets
services_submenu = QtWidgets.QMenu("Services", tray_menu)
services_submenu.menuAction().setVisible(False)
ITrayService._services_submenu = services_submenu

View file

@ -0,0 +1,59 @@
from . import PypeModule, ITrayAction
class ProjectManagerAction(PypeModule, ITrayAction):
label = "Project Manager (beta)"
name = "project_manager"
admin_action = True
def initialize(self, modules_settings):
enabled = False
module_settings = modules_settings.get(self.name)
if module_settings:
enabled = module_settings.get("enabled", enabled)
self.enabled = enabled
# Tray attributes
self.project_manager_window = None
def connect_with_modules(self, *_a, **_kw):
return
def tray_init(self):
"""Initialization in tray implementation of ITrayAction."""
self.create_project_manager_window()
def on_action_trigger(self):
"""Implementation for action trigger of ITrayAction."""
self.show_project_manager_window()
def create_project_manager_window(self):
"""Initializa Settings Qt window."""
if self.project_manager_window:
return
from openpype.tools.project_manager import ProjectManagerWindow
self.project_manager_window = ProjectManagerWindow()
def show_project_manager_window(self):
"""Show project manager tool window.
Raises:
AssertionError: Window must be already created. Call
`create_project_manager_window` before calling this method.
"""
if not self.project_manager_window:
raise AssertionError("Window is not initialized.")
# Store if was visible
was_minimized = self.project_manager_window.isMinimized()
# Show settings gui
self.project_manager_window.show()
if was_minimized:
self.project_manager_window.showNormal()
# Pull window to the front.
self.project_manager_window.raise_()
self.project_manager_window.activateWindow()

View file

@ -37,7 +37,8 @@ class ISettingsChangeListener:
class SettingsAction(PypeModule, ITrayAction):
"""Action to show Setttings tool."""
name = "settings"
label = "Settings"
label = "Studio Settings"
admin_action = True
def initialize(self, _modules_settings):
# This action is always enabled
@ -78,7 +79,7 @@ class SettingsAction(PypeModule, ITrayAction):
Raises:
AssertionError: Window must be already created. Call
`create_settings_window` before callint this method.
`create_settings_window` before calling this method.
"""
if not self.settings_window:
raise AssertionError("Window is not initialized.")
@ -105,7 +106,7 @@ class SettingsAction(PypeModule, ITrayAction):
class LocalSettingsAction(PypeModule, ITrayAction):
"""Action to show Setttings tool."""
name = "local_settings"
label = "Local Settings"
label = "Settings"
def initialize(self, _modules_settings):
# This action is always enabled

View file

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Before After
Before After

View file

@ -164,5 +164,8 @@
},
"standalonepublish_tool": {
"enabled": true
},
"project_manager": {
"enabled": true
}
}

View file

@ -192,6 +192,20 @@
"label": "Enabled"
}
]
},
{
"type": "dict",
"key": "project_manager",
"label": "Project Manager (beta)",
"collapsible": true,
"checkbox_key": "enabled",
"children": [
{
"type": "boolean",
"key": "enabled",
"label": "Enabled"
}
]
}
]
}

View file

@ -504,6 +504,21 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
#IconBtn {}
/* Password dialog*/
#PasswordBtn {
border: none;
padding:0.1em;
background: transparent;
}
#PasswordBtn:hover {
background: {color:bg-buttons};
}
#RememberCheckbox {
spacing: 0.5em;
}
/* Project Manager stylesheets */
#HierarchyView::item {
padding-top: 3px;

View file

@ -11,6 +11,8 @@ from . import (
)
from openpype.style import load_stylesheet
from .style import ResourceCache
from openpype.lib import is_admin_password_required
from openpype.widgets import PasswordDialog
from openpype import resources
from avalon.api import AvalonMongoDB
@ -20,6 +22,10 @@ class ProjectManagerWindow(QtWidgets.QWidget):
def __init__(self, parent=None):
super(ProjectManagerWindow, self).__init__(parent)
self._initial_reset = False
self._password_dialog = None
self._user_passed = False
self.setWindowTitle("OpenPype Project Manager")
self.setWindowIcon(QtGui.QIcon(resources.pype_icon_filepath()))
@ -33,6 +39,9 @@ class ProjectManagerWindow(QtWidgets.QWidget):
project_model = ProjectModel(dbcon)
project_combobox = QtWidgets.QComboBox(project_widget)
project_combobox.setSizeAdjustPolicy(
QtWidgets.QComboBox.AdjustToContents
)
project_combobox.setModel(project_model)
project_combobox.setRootModelIndex(QtCore.QModelIndex())
style_delegate = QtWidgets.QStyledItemDelegate()
@ -135,13 +144,15 @@ class ProjectManagerWindow(QtWidgets.QWidget):
self.resize(1200, 600)
self.setStyleSheet(load_stylesheet())
self.refresh_projects()
def _set_project(self, project_name=None):
self.hierarchy_view.set_project(project_name)
def showEvent(self, event):
super(ProjectManagerWindow, self).showEvent(event)
if not self._initial_reset:
self.reset()
font_size = self._refresh_projects_btn.fontMetrics().height()
icon_size = QtCore.QSize(font_size, font_size)
self._refresh_projects_btn.setIconSize(icon_size)
@ -193,3 +204,45 @@ class ProjectManagerWindow(QtWidgets.QWidget):
project_name = dialog.project_name
self.show_message("Created project \"{}\"".format(project_name))
self.refresh_projects(project_name)
def _show_password_dialog(self):
if self._password_dialog:
self._password_dialog.open()
def _on_password_dialog_close(self, password_passed):
# Store result for future settings reset
self._user_passed = password_passed
# Remove reference to password dialog
self._password_dialog = None
if password_passed:
self.reset()
else:
self.close()
def reset(self):
if self._password_dialog:
return
if not self._user_passed:
self._user_passed = not is_admin_password_required()
if not self._user_passed:
self.setEnabled(False)
# Avoid doubled dialog
dialog = PasswordDialog(self)
dialog.setModal(True)
dialog.finished.connect(self._on_password_dialog_close)
self._password_dialog = dialog
QtCore.QTimer.singleShot(100, self._show_password_dialog)
return
self.setEnabled(True)
# Mark as was reset
if not self._initial_reset:
self._initial_reset = True
self.refresh_projects()

View file

@ -1,11 +1,9 @@
import sys
from Qt import QtWidgets, QtGui
from .lib import (
is_password_required,
BTN_FIXED_SIZE,
CHILD_OFFSET
)
from .widgets import PasswordDialog
from .local_settings import LocalSettingsWindow
from .settings import (
style,
@ -35,13 +33,11 @@ def main(user_role=None):
__all__ = (
"is_password_required",
"BTN_FIXED_SIZE",
"CHILD_OFFSET",
"style",
"PasswordDialog",
"MainWidget",
"ProjectListWidget",
"LocalSettingsWindow",

View file

@ -1,20 +1,2 @@
CHILD_OFFSET = 15
BTN_FIXED_SIZE = 20
def is_password_required():
from openpype.settings import (
get_system_settings,
get_local_settings
)
system_settings = get_system_settings()
password = system_settings["general"].get("admin_password")
if not password:
return False
local_settings = get_local_settings()
is_admin = local_settings.get("general", {}).get("is_admin", False)
if is_admin:
return False
return True

View file

@ -1,10 +1,8 @@
import getpass
from Qt import QtWidgets, QtCore
from openpype.tools.settings import (
is_password_required,
PasswordDialog
)
from openpype.lib import is_admin_password_required
from openpype.widgets import PasswordDialog
class LocalGeneralWidgets(QtWidgets.QWidget):
@ -57,7 +55,7 @@ class LocalGeneralWidgets(QtWidgets.QWidget):
if not self.is_admin_input.isChecked():
return
if not is_password_required():
if not is_admin_password_required():
return
dialog = PasswordDialog(self, False)

View file

@ -7,10 +7,8 @@ from .categories import (
from .widgets import ShadowWidget, RestartDialog
from . import style
from openpype.tools.settings import (
is_password_required,
PasswordDialog
)
from openpype.lib import is_admin_password_required
from openpype.widgets import PasswordDialog
class MainWidget(QtWidgets.QWidget):
@ -117,7 +115,7 @@ class MainWidget(QtWidgets.QWidget):
return
if not self._user_passed:
self._user_passed = not is_password_required()
self._user_passed = not is_admin_password_required()
self._on_state_change()

View file

@ -15,7 +15,11 @@ from openpype.api import (
get_system_settings
)
from openpype.lib import get_pype_execute_args
from openpype.modules import TrayModulesManager, ITrayService
from openpype.modules import (
TrayModulesManager,
ITrayAction,
ITrayService
)
from openpype import style
from .pype_info_widget import PypeInfoWidget
@ -67,6 +71,9 @@ class TrayManager:
self.modules_manager.initialize(self, self.tray_widget.menu)
admin_submenu = ITrayAction.admin_submenu(self.tray_widget.menu)
self.tray_widget.menu.addMenu(admin_submenu)
# Add services if they are
services_submenu = ITrayService.services_submenu(self.tray_widget.menu)
self.tray_widget.menu.addMenu(services_submenu)

View file

@ -0,0 +1,6 @@
from .password_dialog import PasswordDialog
__all__ = (
"PasswordDialog",
)

View file

@ -1,6 +1,7 @@
from Qt import QtWidgets, QtCore, QtGui
from .resources import get_resource
from openpype import style
from openpype.resources import get_resource
from openpype.api import get_system_settings
from openpype.settings.lib import (
@ -43,7 +44,7 @@ class PasswordDialog(QtWidgets.QDialog):
def __init__(self, parent=None, allow_remember=True):
super(PasswordDialog, self).__init__(parent)
self.setWindowTitle("Settings Password")
self.setWindowTitle("Admin Password")
self.resize(300, 120)
system_settings = get_system_settings()
@ -62,13 +63,11 @@ class PasswordDialog(QtWidgets.QDialog):
password_input = QtWidgets.QLineEdit(password_widget)
password_input.setEchoMode(QtWidgets.QLineEdit.Password)
show_password_icon_path = get_resource("images", "eye.png")
show_password_icon_path = get_resource("icons", "eye.png")
show_password_icon = QtGui.QIcon(show_password_icon_path)
show_password_btn = PressHoverButton(password_widget)
show_password_btn.setObjectName("PasswordBtn")
show_password_btn.setIcon(show_password_icon)
show_password_btn.setStyleSheet((
"border: none;padding:0.1em;"
))
show_password_btn.setFocusPolicy(QtCore.Qt.ClickFocus)
password_layout = QtWidgets.QHBoxLayout(password_widget)
@ -83,10 +82,8 @@ class PasswordDialog(QtWidgets.QDialog):
buttons_widget = QtWidgets.QWidget(self)
remember_checkbox = QtWidgets.QCheckBox("Remember", buttons_widget)
remember_checkbox.setObjectName("RememberCheckbox")
remember_checkbox.setVisible(allow_remember)
remember_checkbox.setStyleSheet((
"spacing: 0.5em;"
))
ok_btn = QtWidgets.QPushButton("Ok", buttons_widget)
cancel_btn = QtWidgets.QPushButton("Cancel", buttons_widget)
@ -114,6 +111,8 @@ class PasswordDialog(QtWidgets.QDialog):
self.remember_checkbox = remember_checkbox
self.message_label = message_label
self.setStyleSheet(style.load_stylesheet())
def remember_password(self):
if not self._allow_remember:
return False