Esempio n. 1
0
def __init_websockets(splash, app_bootstrap, settings):
    """
    Initializes the local websocket server.

    :pram splash: Splash widget.
    :param app_bootstrap: The application bootstrap instance.
    :param settings: The application's settings.

    :returns: The tk_framework_desktopserver.Server instance and a boolean indicating if the
        Desktop should keep launching.
    """
    if not __is_64bit_python():
        # Do not import if Python is not 64-bits
        logger.warning("Interpreter is not 64-bits, can't load desktop server")
        return None, True

    if not settings.integration_enabled:
        # Do not import if server is disabled.
        logger.info("Integration was disabled in config.ini.")
        return None, True

    # First try to import the framework. If it fails, let the user decide if the Desktop should
    # keep launching.
    try:
        splash.show()
        splash.set_message("Initializing browser integration")
        # Import framework
        import tk_framework_desktopserver
        app_bootstrap.add_logger_to_logfile(
            tk_framework_desktopserver.get_logger())
    except Exception, e:
        return None, __handle_unexpected_exception_during_websocket_init(
            splash, app_bootstrap, e)
Esempio n. 2
0
def _assert_toolkit_enabled(splash, connection):
    """
    Returns the path to the pipeline configuration for a given site.

    :param splash: Splash dialog
    """
    # get the pipeline configuration for the site we are logged into
    while True:
        pc_schema = connection.schema_entity_read().get(
            "PipelineConfiguration")
        if pc_schema is not None:
            break

        # Toolkit is not turned on show the dialog that explains what to do
        splash.hide()
        dialog = TurnOnToolkit(connection)
        dialog.show()
        dialog.raise_()
        dialog.activateWindow()
        results = dialog.exec_()

        if results == dialog.Rejected:
            # dialog was canceled, raise the exception and let the main exception handler deal
            # with it.
            raise ToolkitDisabledError()

    splash.show()
def _assert_toolkit_enabled(splash, connection):
    """
    Returns the path to the pipeline configuration for a given site.

    :param splash: Splash dialog
    """
    # get the pipeline configuration for the site we are logged into
    while True:
        pc_schema = connection.schema_entity_read().get("PipelineConfiguration")
        if pc_schema is not None:
            break

        # Toolkit is not turned on show the dialog that explains what to do
        splash.hide()
        dialog = TurnOnToolkit(connection)
        dialog.show()
        dialog.raise_()
        dialog.activateWindow()
        results = dialog.exec_()

        if results == dialog.Rejected:
            # dialog was canceled, raise the exception and let the main exception handler deal
            # with it.
            raise ToolkitDisabledError()

    splash.show()
def __init_websockets(splash, app_bootstrap, settings):
    """
    Initializes the local websocket server.

    :pram splash: Splash widget.
    :param app_bootstrap: The application bootstrap instance.
    :param settings: The application's settings.

    :returns: The tk_framework_desktopserver.Server instance and a boolean indicating if the
        Desktop should keep launching.
    """
    if not __is_64bit_python():
        # Do not import if Python is not 64-bits
        logger.warning("Interpreter is not 64-bits, can't load desktop server")
        return None, True

    if not settings.integration_enabled:
        # Do not import if server is disabled.
        logger.info("Integration was disabled in config.ini.")
        return None, True

    # First try to import the framework. If it fails, let the user decide if the Desktop should
    # keep launching.
    try:
        splash.show()
        splash.set_message("Initializing browser integration")
        # Import framework
        import tk_framework_desktopserver
        app_bootstrap.add_logger_to_logfile(tk_framework_desktopserver.get_logger())
    except Exception, e:
        return None, __handle_unexpected_exception_during_websocket_init(splash, app_bootstrap, e)
Esempio n. 5
0
def __launch_app(app, splash, user, app_bootstrap, settings):
    """
    Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally
    updates it and then launches the desktop engine.

    :param app: Application object for event processing.
    :param splash: Splash dialog to update user on what is currently going on
    :param user: Current ShotgunUser.
    :param app_bootstrap: Application bootstrap.
    :param settings: The application's settings.

    :returns: The error code to return to the shell.
    """
    # show the splash screen
    splash.show()

    import sgtk
    sgtk.set_authenticated_user(user)

    # Downloads an upgrade for the startup if available.
    startup_updated = upgrade_startup(
        splash,
        sgtk,
        app_bootstrap
    )
    if startup_updated:
        __restart_app_with_countdown(splash, "Shotgun Desktop updated.")

    splash.set_message("Looking up site configuration.")

    connection = user.create_sg_connection()

    _assert_toolkit_enabled(splash, connection)

    logger.debug("Getting the default site configuration.")
    pc_path, pc, toolkit_classic_required = shotgun_desktop.paths.get_pipeline_configuration_info(connection)

    # We're about to bootstrap, so remove sgtk from our scope so that if we add
    # code that uses it after the bootstrap we have to import the
    # new core.
    del sgtk
    if toolkit_classic_required:
        engine = __start_engine_in_toolkit_classic(app, splash, user, pc, pc_path)
    else:
        engine = __start_engine_in_zero_config(app, app_bootstrap, splash, user)

    return __post_bootstrap_engine(splash, app_bootstrap, engine, settings)
Esempio n. 6
0
def __restart_app_with_countdown(splash, reason):
    """
    Restarts the app after displaying a countdown.

    :param splash: Splash dialog, used to display the countdown.
    :param reason: Reason to display in the dialog for the restart.

    :throws RequestRestartException: This method never returns and throws
    """
    # Provide a countdown so the user knows that the desktop app is being restarted
    # on purpose because of a core update. Otherwise, the user would get a flickering
    # splash screen that from the user point of view looks like the app is redoing work
    # it already did by mistake. This makes the behavior explicit.
    splash.show()
    splash.raise_()
    splash.activateWindow()
    for i in range(3, 0, -1):
        splash.set_message("%s Restarting in %d seconds..." % (reason, i))
        time.sleep(1)
    raise RequestRestartException()
def __restart_app_with_countdown(splash, reason):
    """
    Restarts the app after displaying a countdown.

    :param splash: Splash dialog, used to display the countdown.
    :param reason: Reason to display in the dialog for the restart.

    :throws RequestRestartException: This method never returns and throws
    """
    # Provide a countdown so the user knows that the desktop app is being restarted
    # on purpose because of a core update. Otherwise, the user would get a flickering
    # splash screen that from the user point of view looks like the app is redoing work
    # it already did by mistake. This makes the behavior explicit.
    splash.show()
    splash.raise_()
    splash.activateWindow()
    for i in range(3, 0, -1):
        splash.set_message("%s Restarting in %d seconds..." % (reason, i))
        time.sleep(1)
    raise RequestRestartException()
Esempio n. 8
0
def __launch_app(app, splash, user, app_bootstrap, settings):
    """
    Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally
    updates it and then launches the desktop engine.

    :param app: Application object for event processing.
    :param splash: Splash dialog to update user on what is currently going on
    :param user: Current ShotgunUser.
    :param app_bootstrap: Application bootstrap.
    :param settings: The application's settings.

    :returns: The error code to return to the shell.
    """
    # show the splash screen
    splash.show()

    import sgtk
    sgtk.set_authenticated_user(user)

    # Downloads an upgrade for the startup if available.
    startup_updated = upgrade_startup(splash, sgtk, app_bootstrap)
    if startup_updated:
        # We need to restore the global debug logging setting prior to
        # restarting the app so that forced debug logging is not remembered
        # as the startup state.  Once Desktop relaunches it will resume forced
        # debug logging until core swap, when the original launch setting is
        # restored.
        __restore_global_debug_flag()
        __restart_app_with_countdown(splash, "Shotgun Desktop updated.")

    splash.set_message("Looking up site configuration.")

    connection = user.create_sg_connection()

    _assert_toolkit_enabled(splash, connection)

    logger.debug("Getting the default site configuration.")
    pc_path, pc, toolkit_classic_required = shotgun_desktop.paths.get_pipeline_configuration_info(
        connection)

    # We need to toggle the global debug logging setting back prior to swapping
    # core. Cores older than v0.18.117 do not manage the TK_DEBUG environment
    # variable when global debug is toggled, so we can end up in a situation
    # where the new core in desktopstartup toggles it on, setting the env var,
    # and then when the toggle off occurs we end up with the older core in the
    # site config NOT purging the TK_DEBUG env var. The result is that any
    # subprocesses spawned then end up with debug logging on when the user
    # didn't ask for it. This impacts engine command execution from both Desktop
    # and via browser integration. It is the biggest issue with browser
    # integration, however, as the debug logs are reported back to the web
    # app from the desktopserver RPC API, and the user is presented with a
    # dialog full of debug log messages.
    __restore_global_debug_flag()

    # We're about to bootstrap, so remove sgtk from our scope so that if we add
    # code that uses it after the bootstrap we have to import the
    # new core.
    del sgtk
    if toolkit_classic_required:
        engine = __start_engine_in_toolkit_classic(app, splash, user, pc,
                                                   pc_path)
    else:
        engine = __start_engine_in_zero_config(app, app_bootstrap, splash,
                                               user)

    return __post_bootstrap_engine(splash, app_bootstrap, engine, settings)
Esempio n. 9
0
def __launch_app(app, splash, connection, app_bootstrap, server, settings):
    """
    Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally
    updates it and then launches the desktop engine.

    :param app: Application object for event processing.
    :param splash: Splash dialog to update user on what is currently going on
    :param connection: Connection to the Shotgun server.
    :param server: The tk_framework_desktopserver.Server instance.
    :param settings: The application's settings.

    :returns: The error code to return to the shell.
    """
    # show the splash screen
    splash.show()
    splash.set_message("Looking up site configuration.")
    app.processEvents()

    _assert_toolkit_enabled(splash, connection)

    logger.debug("Getting the default site config")
    default_site_config, pc = shotgun_desktop.paths.get_default_site_config_root(
        connection)

    # try and import toolkit
    toolkit_imported = False
    config_folder_exists_at_startup = os.path.exists(default_site_config)

    reset_site = __extract_command_line_argument("--reset-site")

    # If the config folder exists at startup but the user wants to wipe it, do it.
    if config_folder_exists_at_startup and reset_site:
        logger.info("Resetting site configuration at '%s'" %
                    default_site_config)
        splash.set_message("Resetting site configuration ...")
        shutil.rmtree(default_site_config)
        # It doesn't exist anymore, so we can act as if it never existed in the first place
        config_folder_exists_at_startup = False
        # Remove all occurances of --reset-site so that if we restart the app it doesn't reset it
        # again.

    # If there is no pipeline configuration but we found something on disk nonetheless.
    if not pc and is_toolkit_already_configured(default_site_config):
        raise UnexpectedConfigFound(default_site_config)

    try:
        # In we found a pipeline configuration and the path for the config exists, try to import
        # Toolkit.
        if config_folder_exists_at_startup:
            logger.info("Trying site config from '%s'" % default_site_config)
            sgtk = __import_sgtk_from_path(default_site_config)
            toolkit_imported = True
    except Exception:
        logger.exception("There was an error importing Toolkit:")
        pass
    else:
        # Toolkit was imported, we need to initialize it now.
        if toolkit_imported:
            __initialize_sgtk_authentication(sgtk, app_bootstrap)

    if not toolkit_imported:
        # sgtk not available. initialize core
        logger.info("Import sgtk from site config failed. ")
        try:
            app.processEvents()
            splash.set_message("Initializing Toolkit")
            logger.info("Initializing Toolkit")
            core_path = initialize(splash, connection,
                                   settings.default_app_store_http_proxy)
        except Exception, error:
            logger.exception(error)
            if "ApiUser can not be accessed" in error.message:
                # Login does not have permission to see Scripts, throw an informative
                # error how to work around this for now.
                raise UpdatePermissionsError()
            else:
                raise

        try:
            # try again after the initialization is done
            logger.debug("Importing sgtk after initialization")

            sgtk = __get_initialized_sgtk(core_path, app_bootstrap)

            if sgtk is None:
                # Generate a generic error message, which will suggest to contact support.
                raise Exception("Could not access API post initialization.")

            splash.set_message("Setting up default site configuration...")

            # Install the default site config
            sg = sgtk.util.shotgun.create_sg_connection()

            # Site config has a none project id.
            project_id = None
            # If no pipeline configuration had been found.
            if not pc:
                # This site config has never been set by anyone, so we're the first.
                # If pipeline configurations are still project entities, we'll have to use the
                # TemplateProject as the project which will host the pipeline configuration.
                if does_pipeline_configuration_require_project(connection):
                    template_project = sg.find_one(
                        "Project", [["name", "is", "Template Project"],
                                    ["layout_project", "is", None]])
                    # Can't find template project, so we're effectively done here, we need a project
                    # to create a pipeline configuration.
                    if template_project is None:
                        # Generate a generic error message, which will suggest to contact support.
                        raise Exception(
                            "Error finding the Template project on your site.")

                    logger.info(
                        "Creating the site config using the template project.")

                    # We'll need to use the template project's id to setup the site config in this case.
                    project_id = template_project["id"]
                else:
                    logger.info(
                        "Creating the site config without using a project.")
            else:
                # If a project is set in the pipeline configuration, it's an old style site config tied
                # to the template project, so we have to use it.
                if pc.get("project") is not None:
                    logger.info("Reusing the site config with a project.")
                    project_id = pc["project"]["id"]
                else:
                    logger.info("Reusing the site config without a project.")

            # Create the directory
            if not os.path.exists(default_site_config):
                os.makedirs(default_site_config)

            # Setup the command to create the config
            if sys.platform == "darwin":
                path_param = "config_path_mac"
            elif sys.platform == "win32":
                path_param = "config_path_win"
            elif sys.platform.startswith("linux"):
                path_param = "config_path_linux"

            # allow the config uri to be overridden for testing
            config_uri = os.environ.get("SGTK_SITE_CONFIG_DEBUG_LOCATION",
                                        "tk-config-site")

            params = {
                "auto_path": True,
                "config_uri": config_uri,
                "project_folder_name": "site",
                "project_id": project_id,
                path_param: default_site_config,
            }
            setup_project = sgtk.get_command("setup_project")
            setup_project.set_logger(logger)

            try:
                setup_project.execute(params)
            except Exception, error:
                logger.exception(error)
                if "CRUD ERROR" in error.message:
                    raise UpdatePermissionsError()
                else:
                    raise

            # and now try to load up sgtk through the config again
            sgtk = __get_initialized_sgtk(default_site_config, app_bootstrap)
            tk = sgtk.sgtk_from_path(default_site_config)

            # now localize the core to the config
            splash.set_message("Localizing core...")
            localize = tk.get_command("localize")
            localize.set_logger(logger)
            localize.execute({})

            # Get back the pipeline configuration, this is expected to be initialized further down.
            _, pc = shotgun_desktop.paths.get_default_site_config_root(
                connection)
def __launch_app(app, splash, user, app_bootstrap, settings):
    """
    Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally
    updates it and then launches the desktop engine.

    :param app: Application object for event processing.
    :param splash: Splash dialog to update user on what is currently going on
    :param user: Current ShotgunUser.
    :param app_bootstrap: Application bootstrap.
    :param settings: The application's settings.

    :returns: The error code to return to the shell.
    """
    # show the splash screen
    splash.show()

    import sgtk
    sgtk.set_authenticated_user(user)

    # Downloads an upgrade for the startup if available.
    startup_updated = upgrade_startup(
        splash,
        sgtk,
        app_bootstrap
    )
    if startup_updated:
        __restart_app_with_countdown(splash, "Shotgun Desktop updated.")

    splash.set_message("Looking up site configuration.")

    connection = user.create_sg_connection()

    _assert_toolkit_enabled(splash, connection)

    logger.debug("Getting the default site configuration.")
    pc_path, pc, toolkit_classic_required = shotgun_desktop.paths.get_pipeline_configuration_info(connection)

    # We need to toggle the global debug logging setting back prior to swapping
    # core. Cores older than v0.18.117 do not manage the TK_DEBUG environment
    # variable when global debug is toggled, so we can end up in a situation
    # where the new core in desktopstartup toggles it on, setting the env var,
    # and then when the toggle off occurs we end up with the older core in the
    # site config NOT purging the TK_DEBUG env var. The result is that any
    # subprocesses spawned then end up with debug logging on when the user
    # didn't ask for it. This impacts engine command execution from both Desktop
    # and via browser integration. It is the biggest issue with browser
    # integration, however, as the debug logs are reported back to the web
    # app from the desktopserver RPC API, and the user is presented with a
    # dialog full of debug log messages.
    __restore_global_debug_flag()

    # We're about to bootstrap, so remove sgtk from our scope so that if we add
    # code that uses it after the bootstrap we have to import the
    # new core.
    del sgtk
    if toolkit_classic_required:
        engine = __start_engine_in_toolkit_classic(app, splash, user, pc, pc_path)
    else:
        engine = __start_engine_in_zero_config(app, app_bootstrap, splash, user)

    return __post_bootstrap_engine(splash, app_bootstrap, engine, settings)
def __launch_app(app, splash, connection, app_bootstrap, server):
    """
    Shows the splash screen, optionally downloads and configures Toolkit, imports it, optionally
    updates it and then launches the desktop engine.

    :param app: Application object for event processing.
    :param splash: Splash dialog to update user on what is currently going on
    :param connection: Connection to the Shotgun server.
    :param server: The tk_framework_desktopserver.Server instance.

    :returns: The error code to return to the shell.
    """
    # show the splash screen
    splash.show()
    splash.set_message("Looking up site configuration.")
    app.processEvents()

    _assert_toolkit_enabled(splash, connection)

    logger.debug("Getting the default site config")
    default_site_config, pc = shotgun_desktop.paths.get_default_site_config_root(connection)

    # try and import toolkit
    toolkit_imported = False
    config_folder_exists_at_startup = os.path.exists(default_site_config)

    reset_site = __extract_command_line_argument("--reset-site")

    # If the config folder exists at startup but the user wants to wipe it, do it.
    if config_folder_exists_at_startup and reset_site:
        logger.info("Resetting site configuration at '%s'" % default_site_config)
        splash.set_message("Resetting site configuration ...")
        shutil.rmtree(default_site_config)
        # It doesn't exist anymore, so we can act as if it never existed in the first place
        config_folder_exists_at_startup = False
        # Remove all occurances of --reset-site so that if we restart the app it doesn't reset it
        # again.

    # If there is no pipeline configuration but we found something on disk nonetheless.
    if not pc and is_toolkit_already_configured(default_site_config):
        raise UnexpectedConfigFound(default_site_config)

    try:
        # In we found a pipeline configuration and the path for the config exists, try to import
        # Toolkit.
        if config_folder_exists_at_startup:
            logger.info("Trying site config from '%s'" % default_site_config)
            sgtk = __import_sgtk_from_path(default_site_config)
            toolkit_imported = True
    except Exception:
        logger.exception("There was an error importing Toolkit:")
        pass
    else:
        # Toolkit was imported, we need to initialize it now.
        if toolkit_imported:
            __initialize_sgtk_authentication(sgtk, app_bootstrap)

    if not toolkit_imported:
        # sgtk not available. initialize core
        logger.info("Import sgtk from site config failed. ")
        try:
            app.processEvents()
            splash.set_message("Initializing Toolkit")
            logger.info("Initializing Toolkit")
            core_path = initialize(splash, connection)
        except Exception, error:
            logger.exception(error)
            if "ApiUser can not be accessed" in error.message:
                # Login does not have permission to see Scripts, throw an informative
                # error how to work around this for now.
                raise UpdatePermissionsError()
            else:
                raise

        try:
            # try again after the initialization is done
            logger.debug("Importing sgtk after initialization")

            sgtk = __get_initialized_sgtk(core_path, app_bootstrap)

            if sgtk is None:
                # Generate a generic error message, which will suggest to contact support.
                raise Exception("Could not access API post initialization.")

            splash.set_message("Setting up default site configuration...")

            # Install the default site config
            sg = sgtk.util.shotgun.create_sg_connection()

            # Site config has a none project id.
            project_id = None
            # If no pipeline configuration had been found.
            if not pc:
                # This site config has never been set by anyone, so we're the first.
                # If pipeline configurations are still project entities, we'll have to use the
                # TemplateProject as the project which will host the pipeline configuration.
                if does_pipeline_configuration_require_project(connection):
                    template_project = sg.find_one(
                        "Project",
                        [["name", "is", "Template Project"], ["layout_project", "is", None]])
                    # Can't find template project, so we're effectively done here, we need a project
                    # to create a pipeline configuration.
                    if template_project is None:
                        # Generate a generic error message, which will suggest to contact support.
                        raise Exception("Error finding the Template project on your site.")

                    logger.info("Creating the site config using the template project.")

                    # We'll need to use the template project's id to setup the site config in this case.
                    project_id = template_project["id"]
                else:
                    logger.info("Creating the site config without using a project.")
            else:
                # If a project is set in the pipeline configuration, it's an old style site config tied
                # to the template project, so we have to use it.
                if pc.get("project") is not None:
                    logger.info("Reusing the site config with a project.")
                    project_id = pc["project"]["id"]
                else:
                    logger.info("Reusing the site config without a project.")

            # Create the directory
            if not os.path.exists(default_site_config):
                os.makedirs(default_site_config)

            # Setup the command to create the config
            if sys.platform == "darwin":
                path_param = "config_path_mac"
            elif sys.platform == "win32":
                path_param = "config_path_win"
            elif sys.platform.startswith("linux"):
                path_param = "config_path_linux"

            # allow the config uri to be overridden for testing
            config_uri = os.environ.get("SGTK_SITE_CONFIG_DEBUG_LOCATION", "tk-config-site")

            params = {
                "auto_path": True,
                "config_uri": config_uri,
                "project_folder_name": "site",
                "project_id": project_id,
                path_param: default_site_config,
            }
            setup_project = sgtk.get_command("setup_project")
            setup_project.set_logger(logger)

            try:
                setup_project.execute(params)
            except Exception, error:
                logger.exception(error)
                if "CRUD ERROR" in error.message:
                    raise UpdatePermissionsError()
                else:
                    raise

            # and now try to load up sgtk through the config again
            sgtk = __get_initialized_sgtk(default_site_config, app_bootstrap)
            tk = sgtk.sgtk_from_path(default_site_config)

            # now localize the core to the config
            splash.set_message("Localizing core...")
            localize = tk.get_command("localize")
            localize.set_logger(logger)
            localize.execute({})

            # Get back the pipeline configuration, this is expected to be initialized further down.
            _, pc = shotgun_desktop.paths.get_default_site_config_root(connection)