main
zyy2412666 2023-11-20 15:18:27 +08:00
parent c8ee1b28b3
commit e4824f4ff3
557 changed files with 259186 additions and 0 deletions

View File

@ -0,0 +1,11 @@
<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="flex-container main">
<VisualElement class="form">
<Label name="createdTitle" class="title" />
<Label name="createdExplanation" />
</VisualElement>
</VisualElement>
<VisualElement class="footer">
<Button name="continue" />
</VisualElement>
</UXML>

View File

@ -0,0 +1,14 @@
<UXML xmlns="UnityEngine.UIElements">
<VisualElement name="container">
<Label name="title" />
<Label name="requireMessage" text="This feature requires a Unity Version Control installation." />
<Label name="message" />
<VisualElement name="progressControlsContainer" />
<VisualElement name="footer">
<VisualElement class="grow-max" />
<Button name="downloadCloudEdition" />
<Button name="downloadEnterpriseEdition" />
<Button name="cancel" />
</VisualElement>
</VisualElement>
</UXML>

View File

@ -0,0 +1,29 @@
<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="flex-container column main">
<Label name="confirmationMessage" class="title" />
<VisualElement name="joinSingleOrganization" class="organization-section row collapse">
<Label name="joinSingleOrganizationLabel" class="organization-left" />
<VisualElement class="align-vertical-center">
<Button name="joinSingleOrganizationButton" class="organization-right" />`
</VisualElement>
</VisualElement>
<VisualElement name="joinMultipleOrganizations" class="organization-section collapse">
<Label name="joinMultipleOrganizationsLabel" />
<VisualElement class="row">
<VisualElement name="organizationDropdown" class="organization-left"/>
<Button name="joinMultipleOrganizationsButton" class="organization-right" />
</VisualElement>
</VisualElement>
<VisualElement name="noOrganization" class="organization-section column collapse">
<Label name="noOrganizationLabel" class="organization-left"/>
<Button name="openUnityDashboardButton" class="row">
<Image name="iconUnity" />
</Button>
</VisualElement>
<VisualElement name="progressContainer"/>
</VisualElement>
</UXML>

View File

@ -0,0 +1,10 @@
<UXML xmlns="UnityEngine.UIElements">
<VisualElement name="UpdateNotificationContainer" class="row">
<Image name="UpdateNotificationImage" />
<Label name="UpdateNotificationLabel" />
</VisualElement>
<Button name="UpdateButton" />
<Label name="BranchLabel" class="grow" />
</UXML>

View File

@ -0,0 +1,8 @@
<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="flex-container row">
<VisualElement name="UndefinedProgress" class="collapse" />
<Label name="Progress" />
<Label name="Status" />
<Label name="Percentage" />
</VisualElement>
</UXML>

View File

@ -0,0 +1,18 @@
<UXML xmlns="UnityEngine.UIElements">
<VisualElement name="signInChoiceContainer" class="flex-container column main">
<VisualElement name="signUpContainer" class="row-reverse">
<Button name="signUpButton"/>
<Label name="signUpLabel"/>
</VisualElement>
<Button name="unityIDButton" class="sign-in-buttons">
<Image name="iconUnity" />
</Button>
<Button name="emailButton" class="sign-in-buttons">
<Image name="iconEmail" />
</Button>
</VisualElement>
<VisualElement class="footer">
<Label name="privacyStatementText" />
<Button name="privacyStatement" class="anchor" />
</VisualElement>
</UXML>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<UXML
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="UnityEngine.UIElements"
xsi:noNamespaceSchemaLocation="../UIElementsSchema/UIElements.xsd"
xsi:schemaLocation="UnityEngine.UIElements ../UIElementsSchema/UnityEngine.UIElements.xsd">
<VisualElement name="loginContainer" class="flex-container main">
<Label name="signInLabel" class="title"/>
<TextField name="email"/>
<Label name="emailNotification" class="alert-label"/>
<TextField name="password" password="true"/>
<Label name="passwordNotification" class="alert-label"/>
<VisualElement class="row align-end">
<Button name="back" class="classic-button"/>
<Button name="signIn" class="classic-button"/>
</VisualElement>
<VisualElement name="progressContainer"/>
<VisualElement name="signUpNeededNotificationContainer" class="collapse row">
<Label name="signUpNeededNotificationLabel"/>
<Button name="signUpButton" class="anchor"/>
</VisualElement>
</VisualElement>
</UXML>

View File

@ -0,0 +1,4 @@
<UXML xmlns="UnityEngine.UIElements">
<VisualElement name="TabArea" class="flex-container row" />
<VisualElement name="ContentArea" />
</UXML>

View File

@ -0,0 +1,42 @@
<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="container">
<Label name="plasticConfigurationTitle" />
<Label name="plasticConfigurationExplanation" class="sub-section" />
</VisualElement>
<VisualElement class="container">
<VisualElement class="sub-section">
<Label name="configurationServerInfo"></Label>
<VisualElement name="configurationServerInfoSection">
<VisualElement class="flex-container row">
<TextField name="serverTextField" class="grow" />
<Button name="connect" />
</VisualElement>
<VisualElement class="flex-container row">
<Toggle name="useSslToogle" />
<Label name="useSsl" />
</VisualElement>
<VisualElement>
<Label name="connectedLabel" class="hide" />
</VisualElement>
</VisualElement>
<Label name="configurationUserNameInfo" />
<Label name="configurationCredentialsInfo" class="collapse" />
<VisualElement class="flex-container row">
<TextField name="userTextField" class="credentials" />
<TextField name="passwordTextField" class="credentials collapse" />
<Button name="check" />
<VisualElement name="credentialsPadding" class="grow" />
</VisualElement>
<Label name="credentialsOk" class="hide" />
<VisualElement class="flex-container row">
<VisualElement name="spinnerContainer" class="hide"/>
<Label name="spinnerLabel" class="hide"/>
</VisualElement>
</VisualElement>
</VisualElement>
<VisualElement class="container flex-container row last">
<VisualElement class="grow"></VisualElement>"
<Button name="okButton" />
<Button name="cancelButton" />
</VisualElement>
</UXML>

View File

@ -0,0 +1,10 @@
<UXML xmlns="UnityEngine.UIElements">
<VisualElement class="flex-container main">
<Label name="signInToPlasticSCM" class="title" />
<Label name="completeSignInOnBrowser" />
<VisualElement name="wait" class="flex-container row">
<VisualElement name="progressContainer" class="row" />
<Button name="cancelButton"></Button>
</VisualElement>
</VisualElement>
</UXML>

View File

@ -0,0 +1,140 @@
.collapse {
display: none;
}
.hide {
visibility: hidden;
}
.row {
flex-direction: row;
}
.row-reverse {
flex-direction: row-reverse;
}
.column {
flex-direction: column;
}
.flex-container {
display: flex;
}
.grow {
flex-grow: 1;
}
.grow-max {
flex-grow: 10;
}
.parent-vertical-center{
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.elements-vertical-center{
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.horizontally-centered {
justify-content: center;
}
.align-end {
justify-content: flex-end;
}
.align-center{
justify-content: center;
}
.align-vertical-center{
flex-direction: row;
align-items: center;
}
.align-start {
justify-content: flex-start;
}
.main {
margin-top: 10px;
padding-left: 25px;
padding-right: 20px;
}
.title {
margin: 10px 0px;
font-size: 18px;
}
.error {
color: #FF0000;
}
Button {
cursor: link;
}
.anchor {
color: #2196F3;
padding: 0px;
margin: 0px;
background-color: transparent;
border-width: 0px;
}
.classic-button {
padding: 7px;
margin-top: 10px;
height: 30px;
}
Label {
white-space: normal;
}
.alert-label {
color: red;
}
.footer {
position: absolute;
margin: 20px 10px 0px 10px;
bottom: 5px;
}
.container {
padding: 10px;
border-bottom-width: 1px;
border-bottom-color: #999999;
}
.container.last {
border-bottom-width: 0px;
}
.sub-section {
margin-left: 10px;
}
TextField {
margin-left: 0px;
margin-top: 3px;
margin-right: 10px;
margin-bottom: 3px;
}
Toggle {
margin-left: 0px;
}

View File

@ -0,0 +1,32 @@
@import "Base.uss";
#container {
margin-top: 20px;
margin-right: 20px;
margin-bottom: 20px;
margin-left: 20px;
}
#footer {
display: flex;
flex-direction: row;
margin-top: 15px;
margin-left: 20px;
}
#title {
-unity-font-style: bold;
}
#progressControlsContainer {
height: 15px;
}
Label {
white-space: normal;
margin-bottom: 10px;
}
Button {
width: 180px;
}

View File

@ -0,0 +1,28 @@
@import "Base.uss";
.organization-section {
margin: 10px 0px;
}
.organization-right {
width: auto;
min-width: 70px;
margin-left: 0px;
}
.organization-left{
flex: auto;
margin-top: 3px;
}
#openUnityDashboardButton {
margin-top: 5px;
margin-bottom: 5px;
height: 30px;
color: white;
background-color: black;
}
#joinSingleOrganizationButton{
height: 19px;
}

View File

@ -0,0 +1,7 @@
#ContentArea {
background-color: rgba(48, 48, 48, 255);
}
#StatusBar {
background-color: rgba(25, 25, 25, 255);
}

View File

@ -0,0 +1,7 @@
#ContentArea {
background-color: rgba(184, 184, 184, 255);
}
#StatusBar {
background-color: rgba(138, 138, 138, 255);
}

View File

@ -0,0 +1,62 @@
@import "../Base.uss";
/* Plastic Window Tabview */
#TabView {
height: auto;
min-height: 235px;
flex-grow: 1;
}
#TabArea {
margin: 0px;
margin-top: 5px;
}
.tab-button {
width: 130px;
margin: 0px;
margin-top: 1px;
padding: 1px 16px 2px 16px;
font-size: 13px;
}
/* Notifications, buttons, and settings */
#ControlsContainer {
flex-grow: 1;
justify-content: flex-end;
margin-bottom: 3px;
}
#StatusBar {
flex-grow: 1;
justify-content: flex-end;
height: 22px;
max-height: 22px;
padding: 2px 1px 1px 1px;
}
#UpdateNotificationContainer {
padding-top: 2px;
}
#UpdateNotificationImage {
width: 15px;
height: 15px;
margin-right: 2px;
}
#RefreshButton,
#SettingsButton {
background-color: rgba(1, 1, 1, 0);
border-width: 0px;
padding: 0px;
width: 16px;
height: 16px;
}
#BranchLabel {
-unity-text-align: middle-right;
}

View File

@ -0,0 +1,5 @@
@import "Base.uss";
#Status {
color: red;
}

View File

@ -0,0 +1,25 @@
@import "Base.uss";
.sign-in-buttons {
margin-top: 5px;
margin-bottom: 5px;
height: 30px;
}
#iconUnity, #iconEmail {
width: 32px;
height: 32px
}
#unityIDButton {
color: white;
background-color: black;
}
#signUpLabel{
margin-top: 3px;
}
#privacyStatement {
align-self: flex-start;
}

View File

@ -0,0 +1,32 @@
@import "Base.uss";
#windowContainer {
height: 100%;
flex-grow: 1;
flex-direction: row;
margin-bottom: 30px;
}
#loginContainer TextField {
flex-direction: column;
font-size: 14px;
}
#emailNotification, #passwordNotification {
height: 15px;
margin-bottom: 5px;
}
#progressContainer {
height: 15px;
margin-top: 15px;
}
#signIn,
#back {
width: 125px;
}
#signUpNeededNotificationLabel{
color: red;
}

View File

@ -0,0 +1,31 @@
@import "Base.uss";
#TabView,
#ContentArea {
height: 100%;
}
#TabArea {
margin-top: 10px;
margin-left: 15px;
}
.tab-button {
padding-bottom: 2px;
margin-bottom: 0px;
border-width: 0px;
border-radius: 0px;
font-size: 15px;
background-color: rgba(1, 1, 1, 0);
}
#TabArea .tab-button:hover {
border-bottom-color: #2196F3;
border-bottom-width: 0.25px;
}
#TabArea .tab-button.active {
border-bottom-color: #2196F3;
border-bottom-width: 1.5px;
}

View File

@ -0,0 +1,26 @@
@import "Base.uss";
#configurationServerInfoSection {
margin-bottom: 40px;
}
#spinnerLabel {
margin-left: 2px;
}
#connectedLabel {
-unity-text-align: middle-right;
}
#credentialsOk {
-unity-text-align: middle-right;
}
#plasticConfigurationTitle {
font-size: 18px;
margin-bottom: 10px;
}
.credentials {
width: 265px;
}

View File

@ -0,0 +1,9 @@
#wait {
margin-top: 20px;
padding-top: 40px;
}
#progressContainer {
padding-top: 3px;
margin-right: 50%;
}

View File

@ -0,0 +1,117 @@
using System.IO;
using System.Reflection;
using UnityEditor;
using UnityEngine;
using Codice.Client.Common;
using Codice.Utils;
using PlasticGui;
namespace Unity.PlasticSCM.Editor.AssetUtils
{
internal static class AssetsPath
{
internal static class GetFullPath
{
internal static string ForObject(Object obj)
{
string relativePath = AssetDatabase.GetAssetPath(obj);
if (string.IsNullOrEmpty(relativePath))
return null;
return Path.GetFullPath(relativePath);
}
internal static string ForGuid(string guid)
{
string relativePath = GetAssetPath(guid);
if (string.IsNullOrEmpty(relativePath))
return null;
return Path.GetFullPath(relativePath);
}
}
internal static class GetFullPathUnderWorkspace
{
internal static string ForAsset(
string wkPath,
string assetPath)
{
if (string.IsNullOrEmpty(assetPath))
return null;
string fullPath = Path.GetFullPath(assetPath);
if (!PathHelper.IsContainedOn(fullPath, wkPath))
return null;
return fullPath;
}
internal static string ForGuid(
string wkPath,
string guid)
{
return ForAsset(wkPath, GetAssetPath(guid));
}
}
internal static string GetLayoutsFolderRelativePath()
{
return string.Concat(mAssetsFolderLocation, "/Layouts");
}
internal static string GetStylesFolderRelativePath()
{
return string.Concat(mAssetsFolderLocation, "/Styles");
}
internal static string GetImagesFolderRelativePath()
{
return string.Concat(mAssetsFolderLocation, "/Images");
}
internal static string GetRelativePath(string fullPath)
{
return PathHelper.GetRelativePath(
mProjectFullPath, fullPath).Substring(1);
}
internal static bool IsRunningAsUPMPackage()
{
string unityPlasticDllPath = Path.GetFullPath(
AssemblyLocation.GetAssemblyDirectory(
Assembly.GetAssembly(typeof(PlasticLocalization))));
return Directory.Exists(
Path.GetFullPath(Path.Combine(
unityPlasticDllPath,
// assets relative path when running as a UPM package
"../../../Editor/PlasticSCM/Assets")));
}
static string GetAssetPath(string guid)
{
if (string.IsNullOrEmpty(guid))
return null;
return AssetDatabase.GUIDToAssetPath(guid);
}
static AssetsPath()
{
mAssetsFolderLocation = (IsRunningAsUPMPackage()) ?
"Packages/com.unity.collab-proxy/Editor/PlasticSCM/Assets" :
"Assets/Plugins/PlasticSCM/Editor/Assets";
}
static string mProjectFullPath = ProjectPath.
FromApplicationDataPath(ApplicationDataPath.Get());
static string mAssetsFolderLocation;
}
}

View File

@ -0,0 +1,58 @@
using System.Collections.Generic;
using System.IO;
using Unity.PlasticSCM.Editor.AssetMenu;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using UnityEditor.VersionControl;
namespace Unity.PlasticSCM.Editor.AssetUtils
{
internal static class GetSelectedPaths
{
internal static List<string> ForOperation(
string wkPath,
AssetList assetList,
IAssetStatusCache assetStatusCache,
AssetMenuOperations operation)
{
List<string> selectedPaths = AssetsSelection.
GetSelectedPaths(wkPath, assetList);
List<string> result = new List<string>(selectedPaths);
foreach (string path in selectedPaths)
{
if (MetaPath.IsMetaPath(path))
continue;
string metaPath = MetaPath.GetMetaPath(path);
if (!File.Exists(metaPath))
continue;
if (result.Contains(metaPath))
continue;
if (!IsApplicableForOperation(
metaPath, false, operation, assetStatusCache))
continue;
result.Add(metaPath);
}
return result;
}
static bool IsApplicableForOperation(
string path,
bool isDirectory,
AssetMenuOperations operation,
IAssetStatusCache assetStatusCache)
{
SelectedAssetGroupInfo info = SelectedAssetGroupInfo.BuildFromSingleFile(
path, isDirectory, assetStatusCache);
return AssetMenuUpdater.GetAvailableMenuOperations(info).HasFlag(operation);
}
}
}

View File

@ -0,0 +1,46 @@
using System;
using System.IO;
using UnityEditor;
using Codice.Client.BaseCommands;
namespace Unity.PlasticSCM.Editor.AssetUtils
{
internal static class LoadAsset
{
internal static UnityEngine.Object FromChangeInfo(ChangeInfo changeInfo)
{
string changeFullPath = changeInfo.GetFullPath();
if (MetaPath.IsMetaPath(changeFullPath))
changeFullPath = MetaPath.GetPathFromMetaPath(changeFullPath);
return FromFullPath(changeFullPath);
}
static UnityEngine.Object FromFullPath(string fullPath)
{
if (!IsPathUnderProject(fullPath))
return null;
return AssetDatabase.LoadMainAssetAtPath(
AssetsPath.GetRelativePath(fullPath));
}
static bool IsPathUnderProject(string path)
{
if (string.IsNullOrEmpty(path))
return false;
var fullPath = Path.GetFullPath(path).Replace('\\', '/');
return fullPath.StartsWith(
mProjectRelativePath,
StringComparison.OrdinalIgnoreCase);
}
static string mProjectRelativePath =
Directory.GetCurrentDirectory().Replace('\\', '/') + '/';
}
}

View File

@ -0,0 +1,99 @@
using UnityEditor;
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
using Unity.PlasticSCM.Editor.UI;
using AssetOverlays = Unity.PlasticSCM.Editor.AssetsOverlays;
namespace Unity.PlasticSCM.Editor.AssetUtils.Processor
{
class AssetModificationProcessor : UnityEditor.AssetModificationProcessor
{
internal static bool ForceCheckout { get; private set; }
/* We need to do a checkout, verifying that the content/date or size has changed.
* In order to do this checkout we need the changes to have reached the disk.
* That's why we save the changed files in this array, and when they are reloaded
* in AssetPostprocessor.OnPostprocessAllAssets we process them. */
internal static string[] ModifiedAssets { get; set; }
static AssetModificationProcessor()
{
ForceCheckout = EditorPrefs.GetBool(
UnityConstants.FORCE_CHECKOUT_KEY_NAME);
}
internal static void Enable(
string wkPath,
IAssetStatusCache assetStatusCache)
{
mWkPath = wkPath;
mAssetStatusCache = assetStatusCache;
mIsEnabled = true;
}
internal static void Disable()
{
mIsEnabled = false;
mWkPath = null;
mAssetStatusCache = null;
}
internal static void SetForceCheckoutOption(bool isEnabled)
{
ForceCheckout = isEnabled;
EditorPrefs.SetBool(
UnityConstants.FORCE_CHECKOUT_KEY_NAME,
isEnabled);
}
static string[] OnWillSaveAssets(string[] paths)
{
if (!mIsEnabled)
return paths;
ModifiedAssets = (string[])paths.Clone();
return paths;
}
static bool IsOpenForEdit(string assetPath, out string message)
{
message = string.Empty;
if (!mIsEnabled)
return true;
if (!ForceCheckout)
return true;
if (assetPath.StartsWith("ProjectSettings/"))
return true;
string assetFullPath = AssetsPath.GetFullPathUnderWorkspace.
ForAsset(mWkPath, assetPath);
if (assetFullPath == null)
return true;
if (MetaPath.IsMetaPath(assetFullPath))
assetFullPath = MetaPath.GetPathFromMetaPath(assetFullPath);
AssetOverlays.AssetStatus status = mAssetStatusCache.
GetStatus(assetFullPath);
if (AssetOverlays.ClassifyAssetStatus.IsAdded(status) ||
AssetOverlays.ClassifyAssetStatus.IsCheckedOut(status))
return true;
return !AssetOverlays.ClassifyAssetStatus.IsControlled(status);
}
static bool mIsEnabled;
static IAssetStatusCache mAssetStatusCache;
static string mWkPath;
}
}

View File

@ -0,0 +1,141 @@
using System.Collections.Generic;
using Codice.Client.Common.FsNodeReaders.Watcher;
namespace Unity.PlasticSCM.Editor.AssetUtils.Processor
{
class AssetPostprocessor : UnityEditor.AssetPostprocessor
{
internal struct PathToMove
{
internal readonly string SrcPath;
internal readonly string DstPath;
internal PathToMove(string srcPath, string dstPath)
{
SrcPath = srcPath;
DstPath = dstPath;
}
}
internal static void Enable(
string wkPath,
PlasticAssetsProcessor plasticAssetsProcessor)
{
mWkPath = wkPath;
mPlasticAssetsProcessor = plasticAssetsProcessor;
mIsEnabled = true;
}
internal static void Disable()
{
mIsEnabled = false;
mWkPath = null;
mPlasticAssetsProcessor = null;
}
internal static void SetIsRepaintInspectorNeededAfterAssetDatabaseRefresh()
{
mIsRepaintInspectorNeededAfterAssetDatabaseRefresh = true;
}
static void OnPostprocessAllAssets(
string[] importedAssets,
string[] deletedAssets,
string[] movedAssets,
string[] movedFromAssetPaths)
{
if (!mIsEnabled)
return;
if (mIsRepaintInspectorNeededAfterAssetDatabaseRefresh)
{
mIsRepaintInspectorNeededAfterAssetDatabaseRefresh = false;
RepaintInspector.All();
}
// We need to ensure that the FSWatcher is enabled before processing Plastic operations
// It fixes the following scenario:
// 1. Close PlasticSCM window
// 2. Create an asset, it appears with the added overlay
// 3. Open PlasticSCM window, the asset should appear as added instead of deleted locally
MonoFileSystemWatcher.IsEnabled = true;
mPlasticAssetsProcessor.MoveOnSourceControl(
GetPathsToMoveContainedOnWorkspace(
mWkPath, movedAssets, movedFromAssetPaths));
mPlasticAssetsProcessor.DeleteFromSourceControl(
GetPathsContainedOnWorkspace(mWkPath, deletedAssets));
mPlasticAssetsProcessor.AddToSourceControl(
GetPathsContainedOnWorkspace(mWkPath, importedAssets));
if (AssetModificationProcessor.ModifiedAssets == null)
return;
mPlasticAssetsProcessor.CheckoutOnSourceControl(
GetPathsContainedOnWorkspace(
mWkPath, AssetModificationProcessor.ModifiedAssets));
AssetModificationProcessor.ModifiedAssets = null;
}
static List<PathToMove> GetPathsToMoveContainedOnWorkspace(
string wkPath,
string[] movedAssets,
string[] movedFromAssetPaths)
{
List<PathToMove> result = new List<PathToMove>(
movedAssets.Length);
for (int i = 0; i < movedAssets.Length; i++)
{
string fullSrcPath = AssetsPath.GetFullPathUnderWorkspace.
ForAsset(wkPath, movedFromAssetPaths[i]);
if (fullSrcPath == null)
continue;
string fullDstPath = AssetsPath.GetFullPathUnderWorkspace.
ForAsset(wkPath, movedAssets[i]);
if (fullDstPath == null)
continue;
result.Add(new PathToMove(
fullSrcPath, fullDstPath));
}
return result;
}
static List<string> GetPathsContainedOnWorkspace(
string wkPath, string[] assets)
{
List<string> result = new List<string>(
assets.Length);
foreach (string asset in assets)
{
string fullPath = AssetsPath.GetFullPathUnderWorkspace.
ForAsset(wkPath, asset);
if (fullPath == null)
continue;
result.Add(fullPath);
}
return result;
}
static bool mIsEnabled;
static bool mIsRepaintInspectorNeededAfterAssetDatabaseRefresh;
static PlasticAssetsProcessor mPlasticAssetsProcessor;
static string mWkPath;
}
}

View File

@ -0,0 +1,22 @@
using Unity.PlasticSCM.Editor.AssetsOverlays.Cache;
namespace Unity.PlasticSCM.Editor.AssetUtils.Processor
{
internal static class AssetsProcessors
{
internal static void Enable(
string wkPath,
PlasticAssetsProcessor plasticAssetsProcessor,
IAssetStatusCache assetStatusCache)
{
AssetPostprocessor.Enable(wkPath, plasticAssetsProcessor);
AssetModificationProcessor.Enable(wkPath, assetStatusCache);
}
internal static void Disable()
{
AssetPostprocessor.Disable();
AssetModificationProcessor.Disable();
}
}
}

View File

@ -0,0 +1,107 @@
using System;
using System.Collections.Generic;
using Codice.LogWrapper;
namespace Unity.PlasticSCM.Editor.AssetUtils.Processor
{
internal class PlasticAssetsProcessor : WorkspaceOperationsMonitor.IDisableAssetsProcessor
{
internal void SetWorkspaceOperationsMonitor(
WorkspaceOperationsMonitor workspaceOperationsMonitor)
{
mWorkspaceOperationsMonitor = workspaceOperationsMonitor;
}
internal void AddToSourceControl(List<string> paths)
{
if (paths.Count == 0)
return;
if (IsDisableBecauseExceptionHappened(DateTime.Now))
{
mLog.Warn(
"PlasticAssetsProcessor skipping AddToSourceControl operation " +
"because an exception happened in the last 60 seconds");
return;
}
foreach (string path in paths)
mLog.DebugFormat("AddToSourceControl: {0}", path);
mWorkspaceOperationsMonitor.AddAssetsProcessorPathsToAdd(paths);
}
internal void DeleteFromSourceControl(List<string> paths)
{
if (paths.Count == 0)
return;
if (IsDisableBecauseExceptionHappened(DateTime.Now))
{
mLog.Warn(
"PlasticAssetsProcessor skipping DeleteFromSourceControl operation " +
"because an exception happened in the last 60 seconds");
return;
}
foreach (string path in paths)
mLog.DebugFormat("DeleteFromSourceControl: {0}", path);
mWorkspaceOperationsMonitor.AddAssetsProcessorPathsToDelete(paths);
}
internal void MoveOnSourceControl(List<AssetPostprocessor.PathToMove> paths)
{
if (paths.Count == 0)
return;
if (IsDisableBecauseExceptionHappened(DateTime.Now))
{
mLog.Warn(
"PlasticAssetsProcessor skipping MoveOnSourceControl operation " +
"because an exception happened in the last 60 seconds");
return;
}
foreach (AssetPostprocessor.PathToMove path in paths)
mLog.DebugFormat("MoveOnSourceControl: {0} to {1}", path.SrcPath, path.DstPath);
mWorkspaceOperationsMonitor.AddAssetsProcessorPathsToMove(paths);
}
internal void CheckoutOnSourceControl(List<string> paths)
{
if (paths.Count == 0)
return;
if (IsDisableBecauseExceptionHappened(DateTime.Now))
{
mLog.Warn(
"PlasticAssetsProcessor skipping CheckoutOnSourceControl operation " +
"because an exception happened in the last 60 seconds");
return;
}
foreach (string path in paths)
mLog.DebugFormat("CheckoutOnSourceControl: {0}", path);
mWorkspaceOperationsMonitor.AddAssetsProcessorPathsToCheckout(paths);
}
void WorkspaceOperationsMonitor.IDisableAssetsProcessor.Disable()
{
mLastExceptionDateTime = DateTime.Now;
}
bool IsDisableBecauseExceptionHappened(DateTime now)
{
return (now - mLastExceptionDateTime).TotalSeconds < 5;
}
DateTime mLastExceptionDateTime = DateTime.MinValue;
WorkspaceOperationsMonitor mWorkspaceOperationsMonitor;
static readonly ILog mLog = LogManager.GetLogger("PlasticAssetsProcessor");
}
}

View File

@ -0,0 +1,536 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using Codice.Client.BaseCommands;
using Codice.Client.Commands;
using Codice.Client.Commands.WkTree;
using Codice.LogWrapper;
using GluonGui;
using PlasticGui;
using PlasticGui.WorkspaceWindow;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.Views.IncomingChanges;
using Unity.PlasticSCM.Editor.Views.PendingChanges;
namespace Unity.PlasticSCM.Editor.AssetUtils.Processor
{
internal class WorkspaceOperationsMonitor
{
public interface IDisableAssetsProcessor
{
void Disable();
}
internal WorkspaceOperationsMonitor(
IPlasticAPI plasticApi,
IDisableAssetsProcessor disableAssetsProcessor,
bool isGluonMode)
{
mPlasticAPI = plasticApi;
mDisableAssetsProcessor = disableAssetsProcessor;
mIsGluonMode = isGluonMode;
}
internal void RegisterWindow(
WorkspaceWindow workspaceWindow,
ViewHost viewHost,
NewIncomingChangesUpdater incomingChangesUpdater)
{
mWorkspaceWindow = workspaceWindow;
mViewHost = viewHost;
mNewIncomingChangesUpdater = incomingChangesUpdater;
}
internal void UnRegisterWindow()
{
mWorkspaceWindow = null;
mViewHost = null;
mNewIncomingChangesUpdater = null;
}
internal void RegisterPendingChangesView(
PendingChangesTab pendingChangesTab)
{
mPendingChangesTab = pendingChangesTab;
}
internal void RegisterIncomingChangesView(
IIncomingChangesTab incomingChangesTab)
{
mIncomingChangesTab = incomingChangesTab;
}
internal void UnRegisterViews()
{
mPendingChangesTab = null;
mIncomingChangesTab = null;
}
internal void Start()
{
mIsRunning = true;
Thread thread = new Thread(TaskLoopThread);
thread.IsBackground = true;
thread.Start();
}
internal void Stop()
{
SetAsFinished();
}
internal void AddAssetsProcessorPathsToAdd(
List<string> paths)
{
AddPathsToProcess(
mAssetsProcessorPathsToAdd, paths,
mLock, mResetEvent);
}
internal void AddAssetsProcessorPathsToDelete(
List<string> paths)
{
AddPathsToProcess(
mAssetsProcessorPathsToDelete, paths,
mLock, mResetEvent);
}
internal void AddAssetsProcessorPathsToCheckout(
List<string> paths)
{
AddPathsToProcess(
mAssetsProcessorPathsToCheckout, paths,
mLock, mResetEvent);
}
internal void AddAssetsProcessorPathsToMove(
List<AssetPostprocessor.PathToMove> paths)
{
AddPathsToMoveToProcess(
mAssetsProcessorPathsToMove, paths,
mLock, mResetEvent);
}
internal void AddPathsToCheckout(
List<string> paths)
{
AddPathsToProcess(
mPathsToCheckout, paths,
mLock, mResetEvent);
}
void TaskLoopThread()
{
while (true)
{
try
{
if (!mIsRunning)
break;
ProcessAssetProcessorOperations(
mPlasticAPI,
mAssetsProcessorPathsToAdd,
mAssetsProcessorPathsToDelete,
mAssetsProcessorPathsToCheckout,
mAssetsProcessorPathsToMove,
mLock,
mDisableAssetsProcessor);
ProcessCheckoutOperation(
mPlasticAPI,
mPathsToCheckout,
mLock);
bool hasAssetProcessorOperations = false;
bool hasCheckoutOperations = false;
HasPendingOperationsToProcess(
mAssetsProcessorPathsToAdd,
mAssetsProcessorPathsToDelete,
mAssetsProcessorPathsToCheckout,
mAssetsProcessorPathsToMove,
mPathsToCheckout,
mLock,
out hasAssetProcessorOperations,
out hasCheckoutOperations);
if (hasAssetProcessorOperations ||
hasCheckoutOperations)
continue;
if (!hasAssetProcessorOperations)
EditorDispatcher.Dispatch(AfterAssetProcessorOperation);
if (!hasCheckoutOperations)
EditorDispatcher.Dispatch(AfterCheckoutOperation);
SleepUntilNextWorkload();
}
catch (Exception e)
{
mLog.ErrorFormat(
"Error running the tasks loop : {0}", e.Message);
mLog.DebugFormat(
"Stacktrace: {0}", e.StackTrace);
}
}
}
void AfterAssetProcessorOperation()
{
AutoRefresh.PendingChangesView(
mPendingChangesTab);
AutoRefresh.IncomingChangesView(
mIncomingChangesTab);
}
void AfterCheckoutOperation()
{
RefreshAsset.VersionControlCache();
if (mIsGluonMode)
{
RefreshViewsAfterCheckoutForGluon(mViewHost);
return;
}
if (mNewIncomingChangesUpdater != null)
mNewIncomingChangesUpdater.Update(DateTime.Now);
RefreshViewsAfterCheckoutForDeveloper(mWorkspaceWindow);
}
void SetAsFinished()
{
if (!mIsRunning)
return;
mIsRunning = false;
mResetEvent.Set();
}
void SleepUntilNextWorkload()
{
mResetEvent.Reset();
mResetEvent.WaitOne();
}
static void ProcessAssetProcessorOperations(
IPlasticAPI plasticApi,
List<string> assetsProcessorPathsToAdd,
List<string> assetsProcessorPathsToDelete,
List<string> assetsProcessorPathsToCheckout,
List<AssetPostprocessor.PathToMove> assetsProcessorPathsToMove,
object lockObj,
IDisableAssetsProcessor disableAssetsProcessor)
{
try
{
AssetsProcessorOperations.AddIfNotControlled(
plasticApi, ExtractPathsToProcess(
assetsProcessorPathsToAdd, lockObj),
FilterManager.Get().GetIgnoredFilter());
AssetsProcessorOperations.DeleteIfControlled(
plasticApi, ExtractPathsToProcess(
assetsProcessorPathsToDelete, lockObj));
AssetsProcessorOperations.CheckoutIfControlledAndChanged(
plasticApi, ExtractPathsToProcess(
assetsProcessorPathsToCheckout, lockObj));
AssetsProcessorOperations.MoveIfControlled(
plasticApi, ExtractPathsToMoveToProcess(
assetsProcessorPathsToMove, lockObj));
}
catch (Exception ex)
{
LogException(ex);
disableAssetsProcessor.Disable();
}
}
static void ProcessCheckoutOperation(
IPlasticAPI plasticApi,
List<string> pathsToProcess,
object lockObj)
{
List<string> paths = ExtractPathsToProcess(
pathsToProcess, lockObj);
if (paths.Count == 0)
return;
plasticApi.Checkout(
paths.ToArray(),
CheckoutModifiers.ProcessSymlinks);
}
static void AddPathsToProcess(
List<string> pathsToProcess,
List<string> paths,
object lockObj,
ManualResetEvent resetEvent)
{
lock (lockObj)
{
pathsToProcess.AddRange(paths);
}
resetEvent.Set();
}
static void AddPathsToMoveToProcess(
List<AssetPostprocessor.PathToMove> pathsToProcess,
List<AssetPostprocessor.PathToMove> paths,
object lockObj,
ManualResetEvent resetEvent)
{
lock (lockObj)
{
pathsToProcess.AddRange(paths);
}
resetEvent.Set();
}
static List<string> ExtractPathsToProcess(
List<string> pathsToProcess,
object lockObj)
{
List<string> result;
lock (lockObj)
{
result = new List<string>(pathsToProcess);
pathsToProcess.Clear();
}
return result;
}
static List<AssetPostprocessor.PathToMove> ExtractPathsToMoveToProcess(
List<AssetPostprocessor.PathToMove> pathsToProcess,
object lockObj)
{
List<AssetPostprocessor.PathToMove> result;
lock (lockObj)
{
result = new List<AssetPostprocessor.PathToMove>(pathsToProcess);
pathsToProcess.Clear();
}
return result;
}
static void HasPendingOperationsToProcess(
List<string> assetsProcessorPathsToAdd,
List<string> assetsProcessorPathsToDelete,
List<string> assetsProcessorPathsToCheckout,
List<AssetPostprocessor.PathToMove> assetsProcessorPathsToMove,
List<string> pathsToCheckout,
object lockObj,
out bool hasAssetProcessorOperations,
out bool hasCheckoutOperations)
{
lock (lockObj)
{
hasAssetProcessorOperations =
assetsProcessorPathsToAdd.Count > 0 ||
assetsProcessorPathsToDelete.Count > 0 ||
assetsProcessorPathsToCheckout.Count > 0 ||
assetsProcessorPathsToMove.Count > 0;
hasCheckoutOperations =
pathsToCheckout.Count > 0;
}
}
static void RefreshViewsAfterCheckoutForDeveloper(
IWorkspaceWindow workspaceWindow)
{
if (workspaceWindow == null)
return;
workspaceWindow.RefreshView(ViewType.BranchExplorerView);
workspaceWindow.RefreshView(ViewType.PendingChangesView);
workspaceWindow.RefreshView(ViewType.HistoryView);
}
static void RefreshViewsAfterCheckoutForGluon(
ViewHost viewHost)
{
if (viewHost == null)
return;
viewHost.RefreshView(ViewType.WorkspaceExplorerView);
viewHost.RefreshView(ViewType.CheckinView);
viewHost.RefreshView(ViewType.IncomingChangesView);
viewHost.RefreshView(ViewType.SearchView);
}
static void LogException(Exception ex)
{
mLog.WarnFormat("Message: {0}", ex.Message);
mLog.DebugFormat(
"StackTrace:{0}{1}",
Environment.NewLine, ex.StackTrace);
}
static class AssetsProcessorOperations
{
internal static void AddIfNotControlled(
IPlasticAPI plasticApi,
List<string> paths,
IgnoredFilesFilter ignoredFilter)
{
List<string> result = new List<string>();
foreach (string path in paths)
{
string metaPath = MetaPath.GetMetaPath(path);
if (plasticApi.GetWorkspaceFromPath(path) == null)
return;
if (plasticApi.GetWorkspaceTreeNode(path) == null &&
!ignoredFilter.IsIgnored(path))
result.Add(path);
if (File.Exists(metaPath) &&
plasticApi.GetWorkspaceTreeNode(metaPath) == null &&
!ignoredFilter.IsIgnored(path))
result.Add(metaPath);
}
if (result.Count == 0)
return;
IList checkouts;
plasticApi.Add(
result.ToArray(),
GetDefaultAddOptions(),
out checkouts);
}
internal static void DeleteIfControlled(
IPlasticAPI plasticApi,
List<string> paths)
{
foreach (string path in paths)
{
string metaPath = MetaPath.GetMetaPath(path);
if (plasticApi.GetWorkspaceTreeNode(path) != null)
{
plasticApi.DeleteControlled(
path, DeleteModifiers.None);
}
if (plasticApi.GetWorkspaceTreeNode(metaPath) != null)
{
plasticApi.DeleteControlled(
metaPath, DeleteModifiers.None);
}
}
}
internal static void MoveIfControlled(
IPlasticAPI plasticApi,
List<AssetPostprocessor.PathToMove> paths)
{
foreach (AssetPostprocessor.PathToMove pathToMove in paths)
{
string srcMetaPath = MetaPath.GetMetaPath(pathToMove.SrcPath);
string dstMetaPath = MetaPath.GetMetaPath(pathToMove.DstPath);
if (plasticApi.GetWorkspaceTreeNode(pathToMove.SrcPath) != null)
{
plasticApi.Move(
pathToMove.SrcPath, pathToMove.DstPath,
MoveModifiers.None);
}
if (plasticApi.GetWorkspaceTreeNode(srcMetaPath) != null)
{
plasticApi.Move(
srcMetaPath, dstMetaPath,
MoveModifiers.None);
}
}
}
internal static void CheckoutIfControlledAndChanged(
IPlasticAPI plasticApi,
List<string> paths)
{
List<string> result = new List<string>();
foreach (string path in paths)
{
string metaPath = MetaPath.GetMetaPath(path);
WorkspaceTreeNode node =
plasticApi.GetWorkspaceTreeNode(path);
WorkspaceTreeNode nodeMeta =
plasticApi.GetWorkspaceTreeNode(metaPath);
if (node != null && ChangedFileChecker.IsChanged(
node.LocalInfo, path, false))
result.Add(path);
if (nodeMeta != null && ChangedFileChecker.IsChanged(
nodeMeta.LocalInfo, metaPath, false))
result.Add(metaPath);
}
if (result.Count == 0)
return;
plasticApi.Checkout(
result.ToArray(),
CheckoutModifiers.None);
}
static AddOptions GetDefaultAddOptions()
{
AddOptions options = new AddOptions();
options.AddPrivateParents = true;
options.NeedCheckPlatformPath = true;
return options;
}
}
object mLock = new object();
volatile bool mIsRunning;
volatile ManualResetEvent mResetEvent = new ManualResetEvent(false);
List<string> mAssetsProcessorPathsToAdd = new List<string>();
List<string> mAssetsProcessorPathsToDelete = new List<string>();
List<string> mAssetsProcessorPathsToCheckout = new List<string>();
List<AssetPostprocessor.PathToMove> mAssetsProcessorPathsToMove =
new List<AssetPostprocessor.PathToMove>();
List<string> mPathsToCheckout = new List<string>();
PendingChangesTab mPendingChangesTab;
IIncomingChangesTab mIncomingChangesTab;
NewIncomingChangesUpdater mNewIncomingChangesUpdater;
ViewHost mViewHost;
IWorkspaceWindow mWorkspaceWindow;
readonly bool mIsGluonMode = false;
readonly IDisableAssetsProcessor mDisableAssetsProcessor;
readonly IPlasticAPI mPlasticAPI;
static readonly ILog mLog = LogManager.GetLogger("WorkspaceOperationsMonitor");
}
}

View File

@ -0,0 +1,12 @@
using System.IO;
namespace Unity.PlasticSCM.Editor.AssetUtils
{
internal static class ProjectPath
{
internal static string FromApplicationDataPath(string dataPath)
{
return Path.GetDirectoryName(Path.GetFullPath(dataPath));
}
}
}

View File

@ -0,0 +1,43 @@
using UnityEditor.PackageManager;
using Unity.PlasticSCM.Editor.AssetUtils.Processor;
namespace Unity.PlasticSCM.Editor.AssetUtils
{
internal static class RefreshAsset
{
internal static void BeforeLongAssetOperation()
{
UnityEditor.AssetDatabase.DisallowAutoRefresh();
}
internal static void AfterLongAssetOperation()
{
UnityEditor.AssetDatabase.AllowAutoRefresh();
UnityAssetDatabase();
// Client is an API to interact with package manager
// Client.Resolve() will resolve any pending packages added or removed from the project.
// https://docs.unity3d.com/ScriptReference/PackageManager.Client.html
Client.Resolve();
}
internal static void UnityAssetDatabase()
{
UnityEditor.AssetDatabase.Refresh(
UnityEditor.ImportAssetOptions.Default);
UnityEditor.VersionControl.Provider.ClearCache();
AssetPostprocessor.SetIsRepaintInspectorNeededAfterAssetDatabaseRefresh();
}
internal static void VersionControlCache()
{
UnityEditor.VersionControl.Provider.ClearCache();
RepaintInspector.All();
}
}
}

View File

@ -0,0 +1,17 @@
using UnityEditor;
using UnityEngine;
namespace Unity.PlasticSCM.Editor.AssetUtils
{
internal static class RepaintInspector
{
internal static void All()
{
UnityEditor.Editor[] editors =
Resources.FindObjectsOfTypeAll<UnityEditor.Editor>();
foreach (UnityEditor.Editor editor in editors)
editor.Repaint();
}
}
}

View File

@ -0,0 +1,137 @@
using System.IO;
using System.Collections.Generic;
using UnityEditor;
using UnityEditor.SceneManagement;
using UnityEngine.SceneManagement;
using Codice.Client.BaseCommands;
using Codice.Client.Common;
namespace Unity.PlasticSCM.Editor.AssetUtils
{
internal static class SaveAssets
{
internal static void ForChangesWithConfirmation(
List<ChangeInfo> changes,
out bool isCancelled)
{
ForPaths(
GetPaths(changes), true,
out isCancelled);
}
internal static void ForPathsWithConfirmation(
List<string> paths,
out bool isCancelled)
{
ForPaths(
paths, true,
out isCancelled);
}
internal static void ForChangesWithoutConfirmation(
List<ChangeInfo> changes)
{
bool isCancelled;
ForPaths(
GetPaths(changes), false,
out isCancelled);
}
internal static void ForPathsWithoutConfirmation(
List<string> paths)
{
bool isCancelled;
ForPaths(
paths, false,
out isCancelled);
}
static void ForPaths(
List<string> paths,
bool askForUserConfirmation,
out bool isCancelled)
{
SaveDirtyScenes(
paths,
askForUserConfirmation,
out isCancelled);
if (isCancelled)
return;
AssetDatabase.SaveAssets();
}
static void SaveDirtyScenes(
List<string> paths,
bool askForUserConfirmation,
out bool isCancelled)
{
isCancelled = false;
List<Scene> scenesToSave = new List<Scene>();
foreach (Scene dirtyScene in GetDirtyScenes())
{
if (Contains(paths, dirtyScene))
scenesToSave.Add(dirtyScene);
}
if (scenesToSave.Count == 0)
return;
if (askForUserConfirmation)
{
isCancelled = !EditorSceneManager.
SaveModifiedScenesIfUserWantsTo(
scenesToSave.ToArray());
return;
}
EditorSceneManager.SaveScenes(
scenesToSave.ToArray());
}
static List<Scene> GetDirtyScenes()
{
List<Scene> dirtyScenes = new List<Scene>();
for (int i = 0; i < SceneManager.sceneCount; i++)
{
Scene scene = SceneManager.GetSceneAt(i);
if (!scene.isDirty)
continue;
dirtyScenes.Add(scene);
}
return dirtyScenes;
}
static bool Contains(
List<string> paths,
Scene scene)
{
foreach (string path in paths)
{
if (PathHelper.IsSamePath(
path,
Path.GetFullPath(scene.path)))
return true;
}
return false;
}
static List<string> GetPaths(List<ChangeInfo> changeInfos)
{
List<string> result = new List<string>();
foreach (ChangeInfo change in changeInfos)
result.Add(change.GetFullPath());
return result;
}
}
}

View File

@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using Codice.LogWrapper;
namespace Unity.PlasticSCM.Editor.ProjectDownloader
{
internal static class CloudProjectDownloader
{
internal const string IS_PROJECT_DOWNLOADER_ALREADY_EXECUTED_KEY =
"PlasticSCM.ProjectDownloader.IsAlreadyExecuted";
internal const string SHOULD_PROJECT_BE_DOWNLOADED_KEY =
"PlasticSCM.ProjectDownloader.ShouldProjectBeDownloaded";
internal static void Initialize()
{
EditorApplication.update += RunOnceWhenAccessTokenIsInitialized;
}
static void RunOnceWhenAccessTokenIsInitialized()
{
if (string.IsNullOrEmpty(CloudProjectSettings.accessToken))
return;
EditorApplication.update -= RunOnceWhenAccessTokenIsInitialized;
Execute(CloudProjectSettings.accessToken);
}
static void Execute(string unityAccessToken)
{
if (SessionState.GetBool(
IS_PROJECT_DOWNLOADER_ALREADY_EXECUTED_KEY, false))
{
return;
}
DownloadRepository(unityAccessToken);
SessionState.SetBool(
IS_PROJECT_DOWNLOADER_ALREADY_EXECUTED_KEY, true);
}
internal static void DownloadRepository(string unityAccessToken)
{
DownloadRepository(unityAccessToken, Environment.GetCommandLineArgs());
}
internal static void DownloadRepository(string unityAccessToken, string[] commandLineArgs)
{
Dictionary<string, string> args = CommandLineArguments.Build(commandLineArgs);
mLog.DebugFormat(
"Processing Unity arguments: {0}", string.Join(" ", commandLineArgs));
string projectPath = ParseArguments.ProjectPath(args);
string cloudRepository = ParseArguments.CloudProject(args);
string cloudOrganization = ParseArguments.CloudOrganization(args);
if (string.IsNullOrEmpty(projectPath) ||
string.IsNullOrEmpty(cloudRepository) ||
string.IsNullOrEmpty(cloudOrganization))
{
return;
}
SessionState.SetBool(
SHOULD_PROJECT_BE_DOWNLOADED_KEY, true);
PlasticApp.InitializeIfNeeded();
DownloadRepositoryOperation downloadOperation =
new DownloadRepositoryOperation();
downloadOperation.DownloadRepositoryToPathIfNeeded(
cloudRepository,
cloudOrganization,
Path.GetFullPath(projectPath),
unityAccessToken);
}
static readonly ILog mLog = LogManager.GetLogger("ProjectDownloader");
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
namespace Unity.PlasticSCM.Editor.ProjectDownloader
{
internal class CommandLineArguments
{
internal static Dictionary<string, string> Build(string[] args)
{
Dictionary<string, string> result = new Dictionary<string, string>(
StringComparer.OrdinalIgnoreCase);
if (args == null)
return result;
List<string> trimmedArguments = TrimArgs(args);
int index = 1;
while (true)
{
if (index > trimmedArguments.Count - 1)
break;
if (IsKeyValueArgumentAtIndex(trimmedArguments, index))
{
result[trimmedArguments[index]] = trimmedArguments[index + 1];
index += 2;
continue;
}
result[trimmedArguments[index]] = null;
index += 1;
}
return result;
}
static List<string> TrimArgs(string[] args)
{
List<string> trimmedArguments = new List<string>();
foreach (string argument in args)
trimmedArguments.Add(argument.Trim());
return trimmedArguments;
}
static bool IsKeyValueArgumentAtIndex(
List<string> trimmedArguments,
int index)
{
if (index + 1 > trimmedArguments.Count -1)
return false;
return !trimmedArguments[index + 1].StartsWith("-");
}
}
}

View File

@ -0,0 +1,218 @@
using System;
using System.Threading;
using UnityEditor;
using Codice.Client.BaseCommands;
using Codice.Client.Commands;
using Codice.CM.Common;
using Codice.LogWrapper;
using PlasticGui;
using PlasticGui.WebApi;
using PlasticGui.WorkspaceWindow;
using PlasticGui.WorkspaceWindow.Update;
using Unity.PlasticSCM.Editor.AssetUtils;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.WebApi;
using Unity.PlasticSCM.Editor.Configuration;
namespace Unity.PlasticSCM.Editor.ProjectDownloader
{
internal class DownloadRepositoryOperation
{
internal void DownloadRepositoryToPathIfNeeded(
string cloudRepository,
string cloudOrganization,
string projectPath,
string unityAccessToken)
{
RefreshAsset.BeforeLongAssetOperation();
try
{
BuildProgressSpeedAndRemainingTime.ProgressData progressData =
new BuildProgressSpeedAndRemainingTime.ProgressData(DateTime.Now);
ThreadPool.QueueUserWorkItem(
DownloadRepository,
new DownloadRepositoryParameters()
{
CloudOrganization = cloudOrganization,
CloudRepository = cloudRepository,
ProjectPath = projectPath,
AccessToken = unityAccessToken
});
while (!mOperationFinished)
{
if (mDisplayProgress)
{
DisplayProgress(
mUpdateNotifier.GetUpdateStatus(),
progressData,
cloudRepository);
}
Thread.Sleep(150);
}
}
finally
{
EditorUtility.ClearProgressBar();
RefreshAsset.AfterLongAssetOperation();
if (!mOperationFailed)
{
PlasticPlugin.Enable();
ShowWindow.PlasticAfterDownloadingProject();
}
}
}
void DownloadRepository(object state)
{
DownloadRepositoryParameters parameters = (DownloadRepositoryParameters)state;
try
{
if (FindWorkspace.HasWorkspace(parameters.ProjectPath))
{
// each domain reload, the package is reloaded.
// way need to check if we already downloaded it
return;
}
mDisplayProgress = true;
IPlasticWebRestApi restApi = new PlasticWebRestApi();
string defaultCloudAlias = restApi.GetDefaultCloudAlias();
RepositorySpec repSpec = BuildRepSpec(
parameters.CloudRepository,
parameters.CloudOrganization,
defaultCloudAlias);
TokenExchangeResponse tokenExchangeResponse =
AutoConfig.PlasticCredentials(
parameters.AccessToken,
repSpec.Server,
parameters.ProjectPath);
if (tokenExchangeResponse.Error != null)
{
mOperationFailed = true;
UnityEngine.Debug.LogErrorFormat(
PlasticLocalization.GetString(PlasticLocalization.Name.ErrorDownloadingCloudProject),
string.Format("Unable to get TokenExchangeResponse: {0} [code {1}]",
tokenExchangeResponse.Error.Message,
tokenExchangeResponse.Error.ErrorCode));
return;
}
WorkspaceInfo wkInfo = CreateWorkspace(
repSpec, parameters.ProjectPath);
mLog.DebugFormat("Created workspace {0} on {1}",
wkInfo.Name,
wkInfo.ClientPath);
PlasticGui.Plastic.API.Update(
wkInfo.ClientPath,
UpdateFlags.None,
null,
mUpdateNotifier);
}
catch (Exception ex)
{
LogException(ex);
UnityEngine.Debug.LogErrorFormat(
PlasticLocalization.GetString(PlasticLocalization.Name.ErrorDownloadingCloudProject),
ex.Message);
mOperationFailed = true;
}
finally
{
mOperationFinished = true;
}
}
static void DisplayProgress(
UpdateOperationStatus status,
BuildProgressSpeedAndRemainingTime.ProgressData progressData,
string cloudRepository)
{
string totalProgressMessage = UpdateProgressRender.
GetProgressString(status, progressData);
float totalProgressPercent = GetProgressBarPercent.
ForTransfer(status.UpdatedSize, status.TotalSize) / 100f;
EditorUtility.DisplayProgressBar(
string.Format("{0} {1}",
PlasticLocalization.GetString(PlasticLocalization.Name.DownloadingProgress),
cloudRepository),
totalProgressMessage, totalProgressPercent);
}
static WorkspaceInfo CreateWorkspace(
RepositorySpec repositorySpec,
string projectPath)
{
CreateWorkspaceDialogUserAssistant assistant = new CreateWorkspaceDialogUserAssistant(
PlasticGuiConfig.Get().Configuration.DefaultWorkspaceRoot,
PlasticGui.Plastic.API.GetAllWorkspacesArray());
assistant.RepositoryChanged(
repositorySpec.ToString(),
string.Empty,
string.Empty);
return PlasticGui.Plastic.API.CreateWorkspace(
projectPath,
assistant.GetProposedWorkspaceName(),
repositorySpec.ToString());
}
static RepositorySpec BuildRepSpec(
string cloudRepository,
string cloudOrganization,
string defaultCloudAlias)
{
return new RepositorySpec()
{
Name = cloudRepository,
Server = CloudServer.BuildFullyQualifiedName(
cloudOrganization, defaultCloudAlias)
};
}
static void LogException(Exception ex)
{
mLog.WarnFormat("Message: {0}", ex.Message);
mLog.DebugFormat(
"StackTrace:{0}{1}",
Environment.NewLine, ex.StackTrace);
}
class DownloadRepositoryParameters
{
internal string CloudRepository;
internal string CloudOrganization;
internal string ProjectPath;
internal string AccessToken;
}
volatile bool mOperationFinished = false;
volatile bool mOperationFailed = false;
volatile bool mDisplayProgress;
UpdateNotifier mUpdateNotifier = new UpdateNotifier();
static readonly ILog mLog = LogManager.GetLogger("DownloadRepositoryOperation");
}
}

View File

@ -0,0 +1,53 @@
using System.Collections.Generic;
namespace Unity.PlasticSCM.Editor.ProjectDownloader
{
internal static class ParseArguments
{
internal static string CloudProject(Dictionary<string, string> args)
{
string data;
if (!args.TryGetValue(CLOUD_PROJECT, out data))
return null;
return data;
}
internal static string CloudOrganization(Dictionary<string, string> args)
{
string data;
if (!args.TryGetValue(CLOUD_ORGANIZATION, out data))
return null;
return GetOrganizationNameFromData(data);
}
internal static string ProjectPath(Dictionary<string, string> args)
{
string data;
if (!args.TryGetValue(CREATE_PROJECT, out data))
return null;
return data;
}
static string GetOrganizationNameFromData(string data)
{
// data is in format: 151d73c7-38cb-4eec-b11e-34764e707226-danipen-unity
int guidLenght = 36;
if (data.Length < guidLenght + 1)
return null;
return data.Substring(guidLenght + 1);
}
const string CLOUD_PROJECT = "-cloudProject";
const string CLOUD_ORGANIZATION = "-cloudOrganization";
const string CREATE_PROJECT = "-createProject";
}
}

View File

@ -0,0 +1,264 @@
using System;
using System.IO;
using UnityEditor;
using UnityEngine;
using Codice.Client.Common.Threading;
using Codice.CM.Common;
using Codice.CM.WorkspaceServer;
using Codice.LogWrapper;
using Unity.PlasticSCM.Editor.AssetUtils;
using Unity.PlasticSCM.Editor.WebApi;
using Unity.PlasticSCM.Editor.ProjectDownloader;
namespace Unity.PlasticSCM.Editor.CollabMigration
{
public static class MigrateCollabProject
{
internal static void Initialize()
{
if (SessionState.GetInt(
IS_PROJECT_MIGRATED_ALREADY_CALCULATED_KEY,
MIGRATED_NOT_CALCULATED) == MIGRATED_NOTHING_TO_DO)
return;
EditorApplication.update += RunOnceWhenAccessTokenAndProjectIdAreInitialized;
}
internal static void RunOnceWhenAccessTokenAndProjectIdAreInitialized()
{
if (string.IsNullOrEmpty(CloudProjectSettings.accessToken))
return;
if (!SetupCloudProjectId.HasCloudProjectId())
return;
if (!SessionState.GetBool(
CloudProjectDownloader.IS_PROJECT_DOWNLOADER_ALREADY_EXECUTED_KEY, false))
return;
EditorApplication.update -= RunOnceWhenAccessTokenAndProjectIdAreInitialized;
string projectPath = ProjectPath.FromApplicationDataPath(
ApplicationDataPath.Get());
string projectGuid = SetupCloudProjectId.GetCloudProjectId();
if (!ShouldProjectBeMigrated(projectPath, projectGuid))
{
SessionState.SetInt(
IS_PROJECT_MIGRATED_ALREADY_CALCULATED_KEY,
MIGRATED_NOTHING_TO_DO);
return;
}
Execute(
CloudProjectSettings.accessToken,
projectPath,
projectGuid);
}
static bool ShouldProjectBeMigrated(
string projectPath,
string projectGuid)
{
if (SessionState.GetBool(
CloudProjectDownloader.SHOULD_PROJECT_BE_DOWNLOADED_KEY, false))
{
return false;
}
string collabPath = GetCollabSnapshotFile(
projectPath, projectGuid);
if (!File.Exists(collabPath))
{
return false;
}
if (FindWorkspace.HasWorkspace(ApplicationDataPath.Get()))
{
return false;
}
return true;
}
static void Execute(
string unityAccessToken,
string projectPath,
string projectGuid)
{
string headCommitSha = GetCollabHeadCommitSha(projectPath, projectGuid);
if (string.IsNullOrEmpty(headCommitSha))
return;
PlasticApp.InitializeIfNeeded();
LaunchMigrationIfProjectIsArchivedAndMigrated(
unityAccessToken,
projectPath,
projectGuid,
headCommitSha);
}
internal static void DeletePlasticDirectoryIfExists(string projectPath)
{
WorkspaceInfo wkInfo = new WorkspaceInfo("wk", projectPath);
string plasticDirectory = WorkspaceConfigFile.GetPlasticWkConfigPath(wkInfo);
if (!Directory.Exists(plasticDirectory))
return;
Directory.Delete(plasticDirectory, true);
}
static void LaunchMigrationIfProjectIsArchivedAndMigrated(
string unityAccessToken,
string projectPath,
string projectGuid,
string headCommitSha)
{
IsCollabProjectMigratedResponse isMigratedResponse = null;
ChangesetFromCollabCommitResponse changesetResponse = null;
IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
isMigratedResponse = WebRestApiClient.PlasticScm.
IsCollabProjectMigrated(unityAccessToken, projectGuid);
if (isMigratedResponse.Error != null)
return;
if (!isMigratedResponse.IsMigrated)
return;
OrganizationCredentials credentials = new OrganizationCredentials();
credentials.User = isMigratedResponse.Credentials.Email;
credentials.Password = isMigratedResponse.Credentials.Token;
string webLoginAccessToken = WebRestApiClient.CloudServer.WebLogin(
isMigratedResponse.WebServerUri,
isMigratedResponse.PlasticCloudOrganizationName,
credentials);
changesetResponse = WebRestApiClient.CloudServer.
GetChangesetFromCollabCommit(
isMigratedResponse.WebServerUri,
isMigratedResponse.PlasticCloudOrganizationName,
webLoginAccessToken, projectGuid, headCommitSha);
},
/*afterOperationDelegate*/ delegate
{
if (waiter.Exception != null)
{
ExceptionsHandler.LogException(
"IsCollabProjectArchivedAndMigrated",
waiter.Exception);
return;
}
if (isMigratedResponse.Error != null)
{
mLog.ErrorFormat(
"Unable to get IsCollabProjectMigratedResponse: {0} [code {1}]",
isMigratedResponse.Error.Message,
isMigratedResponse.Error.ErrorCode);
return;
}
if (!isMigratedResponse.IsMigrated)
{
SessionState.SetInt(
IS_PROJECT_MIGRATED_ALREADY_CALCULATED_KEY,
MIGRATED_NOTHING_TO_DO);
return;
}
if (changesetResponse.Error != null)
{
mLog.ErrorFormat(
"Unable to get ChangesetFromCollabCommitResponse: {0} [code {1}]",
changesetResponse.Error.Message,
changesetResponse.Error.ErrorCode);
return;
}
DeletePlasticDirectoryIfExists(projectPath);
MigrationDialog.Show(
null,
unityAccessToken,
projectPath,
isMigratedResponse.Credentials.Email,
isMigratedResponse.PlasticCloudOrganizationName,
new RepId(
changesetResponse.RepId,
changesetResponse.RepModuleId),
changesetResponse.ChangesetId,
changesetResponse.BranchId,
AfterWorkspaceMigrated);
});
}
static void AfterWorkspaceMigrated()
{
SessionState.SetInt(
IS_PROJECT_MIGRATED_ALREADY_CALCULATED_KEY,
MIGRATED_NOTHING_TO_DO);
CollabPlugin.Disable();
mLog.DebugFormat(
"Disabled Collab Plugin after the migration for Project: {0}",
ProjectPath.FromApplicationDataPath(ApplicationDataPath.Get()));
}
static string GetCollabHeadCommitSha(
string projectPath,
string projectGuid)
{
string collabPath = GetCollabSnapshotFile(
projectPath, projectGuid);
if (!File.Exists(collabPath))
return null;
string text = File.ReadAllText(collabPath);
string[] chunks = text.Split(
new string[] { "currRevisionID" },
StringSplitOptions.None);
string current = chunks[1].Substring(3, 40);
if (!current.Contains("none"))
return current;
chunks = text.Split(
new string[] { "headRevisionID" },
StringSplitOptions.None);
return chunks[1].Substring(3, 40);
}
static string GetCollabSnapshotFile(
string projectPath,
string projectGuid)
{
return projectPath + "/Library/Collab/CollabSnapshot_" + projectGuid + ".txt";
}
const string IS_PROJECT_MIGRATED_ALREADY_CALCULATED_KEY =
"PlasticSCM.MigrateCollabProject.IsAlreadyCalculated";
const int MIGRATED_NOT_CALCULATED = 0;
const int MIGRATED_NOTHING_TO_DO = 1;
static readonly ILog mLog = LogManager.GetLogger("MigrateCollabProject");
}
}

View File

@ -0,0 +1,417 @@
using System;
using UnityEditor;
using UnityEngine;
using Codice.Client.BaseCommands;
using Codice.Client.BaseCommands.EventTracking;
using Codice.Client.BaseCommands.Sync;
using Codice.Client.Common.Threading;
using Codice.CM.Common;
using Codice.LogWrapper;
using CodiceApp.EventTracking;
using PlasticGui;
using PlasticGui.WorkspaceWindow;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.Progress;
using Unity.PlasticSCM.Editor.WebApi;
using Unity.PlasticSCM.Editor.Configuration;
namespace Unity.PlasticSCM.Editor.CollabMigration
{
internal class MigrationDialog : PlasticDialog
{
protected override Rect DefaultRect
{
get
{
var baseRect = base.DefaultRect;
return new Rect(baseRect.x, baseRect.y, 710, 260);
}
}
protected override string GetTitle()
{
return "Upgrade your Collaborate project to Unity Version Control";
}
//TODO: localize the strings
protected override void OnModalGUI()
{
GUILayout.BeginHorizontal();
DoIconArea();
GUILayout.Space(20);
DoContentArea();
GUILayout.EndHorizontal();
DoButtonsArea();
mProgressControls.UpdateDeterminateProgress(this);
}
internal static bool Show(
EditorWindow parentWindow,
string unityAccessToken,
string projectPath,
string user,
string organizationName,
RepId repId,
long changesetId,
long branchId,
Action afterWorkspaceMigratedAction)
{
MigrationDialog dialog = Create(
unityAccessToken,
projectPath,
user,
organizationName,
repId,
changesetId,
branchId,
afterWorkspaceMigratedAction,
new ProgressControlsForMigration());
return dialog.RunModal(parentWindow) == ResponseType.Ok;
}
void DoIconArea()
{
GUILayout.BeginVertical();
GUILayout.Space(30);
Rect iconRect = GUILayoutUtility.GetRect(
GUIContent.none, EditorStyles.label,
GUILayout.Width(60), GUILayout.Height(60));
GUI.DrawTexture(
iconRect,
Images.GetPlasticIcon(),
ScaleMode.ScaleToFit);
GUILayout.EndVertical();
}
void DoContentArea()
{
GUILayout.BeginVertical();
Title("Upgrade your Collaborate project to Unity Version Control");
GUILayout.Space(20);
Paragraph("Your Unity project has been upgraded (from Collaborate) to Unity Version Control free" +
" of charge by your administrator. Your local workspace will now be converted to a" +
" Unity Version Control workspace in just a few minutes. Select “Migrate” to start the conversion process.");
DrawProgressForMigration.For(
mProgressControls.ProgressData);
GUILayout.Space(40);
GUILayout.EndVertical();
}
//TODO: localize the strings
void DoButtonsArea()
{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (Application.platform == RuntimePlatform.WindowsEditor)
{
DoOkButton();
DoCloseButton();
return;
}
DoCloseButton();
DoOkButton();
}
}
void DoOkButton()
{
if (mIsMigrationCompleted)
{
DoOpenPlasticButton();
return;
}
DoMigrateButton();
}
void DoCloseButton()
{
GUI.enabled = !mProgressControls.ProgressData.IsOperationRunning;
if (NormalButton(PlasticLocalization.GetString(
PlasticLocalization.Name.CloseButton)))
{
if (mIsMigrationCompleted)
TrackFeatureUseEvent.For(
PlasticGui.Plastic.API.GetRepositorySpec(mWorkspaceInfo),
TrackFeatureUseEvent.Features.CloseDialogAfterWorkspaceMigration);
else
TrackFeatureUseEvent.For(
GetEventCloudOrganizationInfo(),
TrackFeatureUseEvent.Features.DoNotMigrateWorkspace);
CloseButtonAction();
}
GUI.enabled = true;
}
void DoOpenPlasticButton()
{
if (!NormalButton("Open Unity Version Control"))
return;
TrackFeatureUseEvent.For(
PlasticGui.Plastic.API.GetRepositorySpec(mWorkspaceInfo),
TrackFeatureUseEvent.Features.OpenPlasticAfterWorkspaceMigration);
((IPlasticDialogCloser)this).CloseDialog();
ShowWindow.Plastic();
}
EventCloudOrganizationInfo GetEventCloudOrganizationInfo()
{
return new EventCloudOrganizationInfo()
{
Name = mOrganizationName,
ServerType = EventCloudOrganizationInfo.GetServerType(true),
User = mUser
};
}
void DoMigrateButton()
{
GUI.enabled = !mProgressControls.ProgressData.IsOperationRunning;
if (NormalButton("Migrate"))
{
if (EditorUtility.DisplayDialog(
"Collab migration to Unity Version Control",
"Are you sure to start the migration process?",
PlasticLocalization.GetString(PlasticLocalization.Name.YesButton),
PlasticLocalization.GetString(PlasticLocalization.Name.NoButton)))
{
TrackFeatureUseEvent.For(
GetEventCloudOrganizationInfo(),
TrackFeatureUseEvent.Features.MigrateWorkspace);
LaunchMigration(
mUnityAccessToken, mProjectPath,
mOrganizationName, mRepId,
mChangesetId, mBranchId,
mAfterWorkspaceMigratedAction,
mProgressControls);
}
else
{
TrackFeatureUseEvent.For(
GetEventCloudOrganizationInfo(),
TrackFeatureUseEvent.Features.DoNotMigrateWorkspace);
}
}
GUI.enabled = true;
}
static void UpdateProgress(string wkPath,
CreateWorkspaceFromCollab.Progress progress,
ProgressControlsForMigration progressControls,
BuildProgressSpeedAndRemainingTime.ProgressData progressData)
{
string header = MigrationProgressRender.FixNotificationPath(
wkPath, progress.CurrentFile);
string message = MigrationProgressRender.GetProgressString(
progress,
progressData,
DateTime.Now,
0.05,
"Calculating...",
"Converted {0} of {1}bytes ({2} of 1 file){4}",
"Converted {0} of {1}bytes ({2} of {3} files {4})",
"remaining");
float percent = GetProgressBarPercent.ForTransfer(
progress.ProcessedSize, progress.TotalSize) / 100f;
progressControls.ShowProgress(header, message, percent);
}
void LaunchMigration(
string unityAccessToken,
string projectPath,
string organizationName,
RepId repId,
long changesetId,
long branchId,
Action afterWorkspaceMigratedAction,
ProgressControlsForMigration progressControls)
{
string serverName = string.Format(
"{0}@cloud", organizationName);
TokenExchangeResponse tokenExchangeResponse = null;
mWorkspaceInfo = null;
CreateWorkspaceFromCollab.Progress progress = new CreateWorkspaceFromCollab.Progress();
BuildProgressSpeedAndRemainingTime.ProgressData progressData =
new BuildProgressSpeedAndRemainingTime.ProgressData(DateTime.Now);
IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
waiter.Execute(
/*threadOperationDelegate*/
delegate
{
tokenExchangeResponse = AutoConfig.PlasticCredentials(
unityAccessToken,
serverName,
projectPath);
if (tokenExchangeResponse.Error != null)
{
return;
}
RepositoryInfo repInfo = new BaseCommandsImpl().
GetRepositoryInfo(repId, serverName);
if (repInfo == null)
{
return;
}
repInfo.SetExplicitServer(serverName);
mWorkspaceInfo = CreateWorkspaceFromCollab.Create(
projectPath, repInfo.Name, repInfo,
changesetId, branchId,
progress);
},
/*afterOperationDelegate*/
delegate
{
progressControls.HideProgress();
if (waiter.Exception != null)
{
DisplayException(progressControls, waiter.Exception);
TrackWorkspaceMigrationFinishedFailureEvent(mWorkspaceInfo);
return;
}
if (tokenExchangeResponse.Error != null)
{
mLog.ErrorFormat(
"Unable to get TokenExchangeResponse: {0} [code {1}]",
tokenExchangeResponse.Error.Message,
tokenExchangeResponse.Error.ErrorCode);
}
if (tokenExchangeResponse.Error != null ||
mWorkspaceInfo == null)
{
progressControls.ShowError(
"Failed to convert your workspace to Unity Version Control");
TrackWorkspaceMigrationFinishedFailureEvent(mWorkspaceInfo);
return;
}
progressControls.ShowSuccess(
"Your workspace has been successfully converted to Unity Version Control");
mIsMigrationCompleted = true;
TrackFeatureUseEvent.For(
PlasticGui.Plastic.API.GetRepositorySpec(mWorkspaceInfo),
TrackFeatureUseEvent.Features.WorkspaceMigrationFinishedSuccess);
afterWorkspaceMigratedAction();
},
/*timerTickDelegate*/
delegate
{
UpdateProgress(projectPath, progress, progressControls, progressData);
});
}
void TrackWorkspaceMigrationFinishedFailureEvent(WorkspaceInfo wkInfo)
{
if (wkInfo == null)
{
TrackFeatureUseEvent.For(
GetEventCloudOrganizationInfo(),
TrackFeatureUseEvent.Features.WorkspaceMigrationFinishedFailure);
return;
}
TrackFeatureUseEvent.For(
PlasticGui.Plastic.API.GetRepositorySpec(wkInfo),
TrackFeatureUseEvent.Features.WorkspaceMigrationFinishedFailure);
}
static void DisplayException(
ProgressControlsForMigration progressControls,
Exception ex)
{
ExceptionsHandler.LogException(
"MigrationDialog", ex);
progressControls.ShowError(
ExceptionsHandler.GetCorrectExceptionMessage(ex));
}
static MigrationDialog Create(
string unityAccessToken,
string projectPath,
string user,
string organizationName,
RepId repId,
long changesetId,
long branchId,
Action afterWorkspaceMigratedAction,
ProgressControlsForMigration progressControls)
{
var instance = CreateInstance<MigrationDialog>();
instance.IsResizable = false;
instance.mUnityAccessToken = unityAccessToken;
instance.mProjectPath = projectPath;
instance.mUser = user;
instance.mOrganizationName = organizationName;
instance.mRepId = repId;
instance.mChangesetId = changesetId;
instance.mBranchId = branchId;
instance.mAfterWorkspaceMigratedAction = afterWorkspaceMigratedAction;
instance.mProgressControls = progressControls;
instance.mEscapeKeyAction = instance.CloseButtonAction;
return instance;
}
bool mIsMigrationCompleted;
ProgressControlsForMigration mProgressControls;
Action mAfterWorkspaceMigratedAction;
long mChangesetId;
long mBranchId;
RepId mRepId;
string mOrganizationName;
string mUser;
string mProjectPath;
string mUnityAccessToken;
WorkspaceInfo mWorkspaceInfo;
static readonly ILog mLog = LogManager.GetLogger("MigrationDialog");
}
}

View File

@ -0,0 +1,73 @@
using System;
using System.IO;
using Codice.Client.Commands;
using Codice.Client.Common;
using Codice.Client.BaseCommands;
using Codice.LogWrapper;
using PlasticGui;
using Codice.Client.BaseCommands.Sync;
namespace Unity.PlasticSCM.Editor.CollabMigration
{
internal class MigrationProgressRender
{
internal static string FixNotificationPath(string wkPath, string notification)
{
if (notification == null)
return string.Empty;
int position = notification.ToLower().IndexOf(wkPath.ToLower());
if (position < 0)
return notification;
return notification.Remove(position, wkPath.Length + 1);
}
internal static string GetProgressString(
CreateWorkspaceFromCollab.Progress status,
BuildProgressSpeedAndRemainingTime.ProgressData progressData,
DateTime now,
double smoothingFactor,
string updateProgressCalculatingMessage,
string updateProgressSingularMessage,
string updateProgressPluralMessage,
string remainingMessage)
{
if (status.CurrentStatus == CreateWorkspaceFromCollab.Progress.Status.Starting)
return updateProgressCalculatingMessage;
progressData.StartTimerIfNotStarted(now);
string updatedSize;
string totalSize;
GetFormattedSizes.ForTransfer(
status.ProcessedSize,
status.TotalSize,
out updatedSize,
out totalSize);
string details = string.Format(
status.TotalFiles == 1 ?
updateProgressSingularMessage :
updateProgressPluralMessage,
updatedSize,
totalSize,
status.ProcessedFiles,
status.TotalFiles,
BuildProgressSpeedAndRemainingTime.ForTransfer(
progressData,
now,
status.TotalSize,
status.ProcessedSize,
smoothingFactor,
remainingMessage));
return details;
}
static ILog mLog = LogManager.GetLogger("MigrationProgressRender");
}
}

View File

@ -0,0 +1,137 @@
using Codice.Client.Common;
using Codice.CM.Common;
using PlasticGui;
using Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome;
using Unity.PlasticSCM.Editor.WebApi;
namespace Unity.PlasticSCM.Editor.Configuration
{
internal static class AutoConfig
{
internal static TokenExchangeResponse PlasticCredentials(
string unityAccessToken,
string serverName,
string projectPath)
{
SetupUnityEditionToken.CreateCloudEditionTokenIfNeeded();
bool isClientConfigConfigured = ClientConfig.IsConfigured();
if (!isClientConfigConfigured)
{
ConfigureClientConf.FromUnityAccessToken(
unityAccessToken, serverName, projectPath);
}
TokenExchangeResponse tokenExchangeResponse = WebRestApiClient.
PlasticScm.TokenExchange(unityAccessToken);
if (tokenExchangeResponse.Error != null)
return tokenExchangeResponse;
CloudEditionWelcomeWindow.JoinCloudServer(
serverName,
tokenExchangeResponse.User,
tokenExchangeResponse.AccessToken);
if (!isClientConfigConfigured)
return tokenExchangeResponse;
ConfigureProfile.ForServerIfNeeded(
serverName,
tokenExchangeResponse.User);
return tokenExchangeResponse;
}
static class ConfigureClientConf
{
internal static void FromUnityAccessToken(
string unityAccessToken,
string serverName,
string projectPath)
{
CredentialsResponse response = WebRestApiClient.
PlasticScm.GetCredentials(unityAccessToken);
if (response.Error != null)
{
UnityEngine.Debug.LogErrorFormat(
PlasticLocalization.GetString(
PlasticLocalization.Name.ErrorGettingCredentialsCloudProject),
response.Error.Message,
response.Error.ErrorCode);
return;
}
ClientConfigData configData = BuildClientConfigData(
serverName, projectPath, response);
ClientConfig.Get().Save(configData);
}
static ClientConfigData BuildClientConfigData(
string serverName,
string projectPath,
CredentialsResponse response)
{
SEIDWorkingMode workingMode = GetWorkingMode(response.Type);
ClientConfigData configData = new ClientConfigData();
configData.WorkspaceServer = serverName;
configData.CurrentWorkspace = projectPath;
configData.WorkingMode = workingMode.ToString();
configData.SecurityConfig = UserInfo.GetSecurityConfigStr(
workingMode,
response.Email,
GetPassword(response.Token, response.Type));
configData.LastRunningEdition = InstalledEdition.Get();
return configData;
}
static string GetPassword(
string token,
CredentialsResponse.TokenType tokenType)
{
if (tokenType == CredentialsResponse.TokenType.Bearer)
return BEARER_PREFIX + token;
return token;
}
static SEIDWorkingMode GetWorkingMode(CredentialsResponse.TokenType tokenType)
{
if (tokenType == CredentialsResponse.TokenType.Bearer)
return SEIDWorkingMode.SSOWorkingMode;
return SEIDWorkingMode.LDAPWorkingMode;
}
const string BEARER_PREFIX = "Bearer ";
}
static class ConfigureProfile
{
internal static void ForServerIfNeeded(string serverName, string user)
{
ProfileManager profileManager = CmConnection.Get().GetProfileManager();
ServerProfile serverProfile = profileManager.GetProfileForServer(serverName);
if (serverProfile != null)
return;
serverProfile = ProfileManager.CreateProfile(
serverName,
SEIDWorkingMode.SSOWorkingMode,
user);
profileManager.SaveProfile(serverProfile);
}
}
}
}

View File

@ -0,0 +1,93 @@
using Codice.Client.Common;
using Codice.CM.Common;
using PlasticGui;
using PlasticPipe.Certificates;
using Unity.PlasticSCM.Editor.UI;
using UnityEditor;
namespace Unity.PlasticSCM.Editor.Configuration
{
internal class ChannelCertificateUiImpl : IChannelCertificateUI
{
internal ChannelCertificateUiImpl()
{
}
CertOperationResult IChannelCertificateUI.AcceptNewServerCertificate(PlasticCertInfo serverCertificate)
{
return GetUserResponse(
PlasticLocalization.GetString(
PlasticLocalization.Name.NewCertificateTitle),
PlasticLocalization.GetString(
PlasticLocalization.Name.NewCertificateMessageUnityVCS),
serverCertificate);
}
CertOperationResult IChannelCertificateUI.AcceptChangedServerCertificate(PlasticCertInfo serverCertificate)
{
return GetUserResponse(
PlasticLocalization.GetString(
PlasticLocalization.Name.ExistingCertificateChangedTitle),
PlasticLocalization.GetString(
PlasticLocalization.Name.ExistingCertificateChangedMessageUnityVCS),
serverCertificate);
}
bool IChannelCertificateUI.AcceptInvalidHostname(string certHostname, string serverHostname)
{
bool result = false;
GUIActionRunner.RunGUIAction(delegate {
result = EditorUtility.DisplayDialog(
PlasticLocalization.GetString(
PlasticLocalization.Name.InvalidCertificateHostnameTitle),
PlasticLocalization.GetString(
PlasticLocalization.Name.InvalidCertificateHostnameMessage,
certHostname, serverHostname),
PlasticLocalization.GetString(PlasticLocalization.Name.YesButton),
PlasticLocalization.GetString(PlasticLocalization.Name.NoButton));
});
return result;
}
CertOperationResult GetUserResponse(
string title, string message, PlasticCertInfo serverCertificate)
{
GuiMessage.GuiMessageResponseButton result =
GuiMessage.GuiMessageResponseButton.Neutral;
GUIActionRunner.RunGUIAction(delegate {
result = GuiMessage.ShowQuestion(
title,
GetCertificateMessageString(message, serverCertificate),
PlasticLocalization.GetString(PlasticLocalization.Name.YesButton),
PlasticLocalization.GetString(PlasticLocalization.Name.CancelButton),
PlasticLocalization.GetString(PlasticLocalization.Name.NoButton));
});
switch (result)
{
case GuiMessage.GuiMessageResponseButton.Positive:
return CertOperationResult.AddToStore;
case GuiMessage.GuiMessageResponseButton.Negative:
return CertOperationResult.DoNotAddToStore;
case GuiMessage.GuiMessageResponseButton.Neutral:
return CertOperationResult.Cancel;
default:
return CertOperationResult.Cancel;
}
}
string GetCertificateMessageString(string message, PlasticCertInfo serverCertificate)
{
return string.Format(message,
CertificateUi.GetCnField(serverCertificate.Subject),
CertificateUi.GetCnField(serverCertificate.Issuer),
serverCertificate.Format,
serverCertificate.ExpirationDateString,
serverCertificate.KeyAlgorithm,
serverCertificate.CertHashString);
}
}
}

View File

@ -0,0 +1,199 @@
using System;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using Codice.Client.Common.Threading;
using Codice.LogWrapper;
using PlasticGui.Configuration.OAuth;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.Progress;
using Unity.PlasticSCM.Editor.WebApi;
namespace Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome
{
internal class AutoLogin : OAuthSignIn.INotify
{
internal enum State : byte
{
Off = 0,
Started = 1,
Running = 2,
ResponseInit = 3,
ResponseEnd = 6,
ResponseSuccess = 7,
OrganizationChoosed = 8,
InitializingPlastic = 9,
ErrorNoToken = 20,
ErrorTokenException = 21,
ErrorResponseNull = 22,
ErrorResponseError = 23,
ErrorTokenEmpty = 24,
ErrorResponseCancel = 25
}
internal string AccessToken;
internal string UserName;
internal void Run()
{
mPlasticWindow = GetPlasticWindow();
if (!string.IsNullOrEmpty(CloudProjectSettings.accessToken))
{
ExchangeTokensAndJoinOrganization(CloudProjectSettings.accessToken);
return;
}
mPlasticWindow.GetWelcomeView().autoLoginState = AutoLogin.State.ErrorNoToken;
}
void OAuthSignIn.INotify.SuccessForConfigure(
List<string> organizations,
bool canCreateAnOrganization,
string userName,
string accessToken)
{
mPlasticWindow.GetWelcomeView().autoLoginState = AutoLogin.State.ResponseSuccess;
ChooseOrganization(organizations);
}
void OAuthSignIn.INotify.SuccessForSSO(string organization)
{
}
void OAuthSignIn.INotify.SuccessForProfile(string email)
{
}
void OAuthSignIn.INotify.SuccessForHomeView(string userName)
{
}
void OAuthSignIn.INotify.SuccessForCredentials(
string email,
string accessToken)
{
}
void OAuthSignIn.INotify.Cancel(string errorMessage)
{
mPlasticWindow.GetWelcomeView().autoLoginState = AutoLogin.State.ErrorResponseCancel;
}
void ExchangeTokensAndJoinOrganization(string unityAccessToken)
{
int ini = Environment.TickCount;
TokenExchangeResponse response = null;
IThreadWaiter waiter = ThreadWaiter.GetWaiter(10);
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
mPlasticWindow.GetWelcomeView().autoLoginState = AutoLogin.State.ResponseInit;
response = WebRestApiClient.PlasticScm.TokenExchange(unityAccessToken);
},
/*afterOperationDelegate*/ delegate
{
mLog.DebugFormat(
"TokenExchange time {0} ms",
Environment.TickCount - ini);
if (waiter.Exception != null)
{
mPlasticWindow.GetWelcomeView().autoLoginState = AutoLogin.State.ErrorTokenException;
ExceptionsHandler.LogException(
"TokenExchangeSetting",
waiter.Exception);
Debug.LogWarning(waiter.Exception.Message);
return;
}
if (response == null)
{
mPlasticWindow.GetWelcomeView().autoLoginState = AutoLogin.State.ErrorResponseNull;
Debug.LogWarning("Auto Login response null");
return;
}
if (response.Error != null)
{
mPlasticWindow.GetWelcomeView().autoLoginState = AutoLogin.State.ErrorResponseError;
var warning = string.Format(
"Unable to exchange token: {0} [code {1}]",
response.Error.Message, response.Error.ErrorCode);
mLog.ErrorFormat(warning);
Debug.LogWarning(warning);
return;
}
if (string.IsNullOrEmpty(response.AccessToken))
{
mPlasticWindow.GetWelcomeView().autoLoginState = AutoLogin.State.ErrorTokenEmpty;
var warning = string.Format(
"Access token is empty for user: {0}",
response.User);
mLog.InfoFormat(warning);
Debug.LogWarning(warning);
return;
}
mPlasticWindow.GetWelcomeView().autoLoginState = AutoLogin.State.ResponseEnd;
AccessToken = response.AccessToken;
UserName = response.User;
GetOrganizationList();
});
}
void GetOrganizationList()
{
OAuthSignIn.GetOrganizationsFromAccessToken(
string.Empty,
CloudProjectSettings.userName,
AccessToken,
OAuthSignIn.Mode.Configure,
new ProgressControlsForDialogs(),
this,
PlasticGui.Plastic.WebRestAPI
);
}
void ChooseOrganization(
List<string> organizations)
{
mPlasticWindow = GetPlasticWindow();
CloudEditionWelcomeWindow.ShowWindow(
PlasticGui.Plastic.WebRestAPI,
mPlasticWindow.CmConnectionForTesting, null, true);
mCloudEditionWelcomeWindow = CloudEditionWelcomeWindow.GetWelcomeWindow();
mCloudEditionWelcomeWindow.FillUserAndToken(UserName, AccessToken);
if (organizations.Count == 1)
{
mCloudEditionWelcomeWindow.JoinOrganizationAndWelcomePage(organizations[0]);
return;
}
mCloudEditionWelcomeWindow.ShowOrganizationPanelFromAutoLogin(organizations);
}
static PlasticWindow GetPlasticWindow()
{
var windows = Resources.FindObjectsOfTypeAll<PlasticWindow>();
PlasticWindow plasticWindow = windows.Length > 0 ? windows[0] : null;
if (plasticWindow == null)
plasticWindow = ShowWindow.Plastic();
return plasticWindow;
}
PlasticWindow mPlasticWindow;
CloudEditionWelcomeWindow mCloudEditionWelcomeWindow;
static readonly ILog mLog = LogManager.GetLogger("TokensExchange");
}
}

View File

@ -0,0 +1,273 @@
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using PlasticGui;
using PlasticGui.WebApi;
using Unity.PlasticSCM.Editor.UI.UIElements;
using PlasticGui.Configuration.CloudEdition.Welcome;
using PlasticGui.Configuration.OAuth;
using System.Collections.Generic;
using Codice.Client.Common.Servers;
using Codice.Client.Common;
using Codice.Utils;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.Views.Welcome;
using Codice.CM.Common;
namespace Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome
{
internal interface IWelcomeWindowNotify
{
void SuccessForConfigure(List<string> organizations);
void Back();
}
internal class CloudEditionWelcomeWindow :
EditorWindow,
OAuthSignIn.INotify,
IWelcomeWindowNotify
{
internal static void ShowWindow(
IPlasticWebRestApi restApi,
CmConnection cmConnection,
WelcomeView welcomeView,
bool autoLogin = false)
{
sRestApi = restApi;
sCmConnection = cmConnection;
sAutoLogin = autoLogin;
CloudEditionWelcomeWindow window = GetWindow<CloudEditionWelcomeWindow>();
window.titleContent = new GUIContent(
PlasticLocalization.GetString(PlasticLocalization.Name.SignInToUnityVCS));
window.minSize = window.maxSize = new Vector2(450, 300);
window.mWelcomeView = welcomeView;
window.Show();
}
internal static CloudEditionWelcomeWindow GetWelcomeWindow()
{
return GetWindow<CloudEditionWelcomeWindow>();
}
void OnEnable()
{
BuildComponents();
}
internal void CancelJoinOrganization()
{
if (sAutoLogin)
{
GetWindow<PlasticWindow>().GetWelcomeView().autoLoginState = AutoLogin.State.Started;
}
}
internal void JoinOrganizationAndWelcomePage(string organization)
{
JoinCloudServer(organization,
mUserName,
mAccessToken);
GetWelcomePage.Run(sRestApi, organization);
}
internal static void JoinCloudServer(
string cloudServer,
string username,
string accessToken)
{
SaveCloudServer.ToPlasticGuiConfig(cloudServer);
SaveCloudServer.ToPlasticGuiConfigFile(
cloudServer, GetPlasticConfigFileToSaveOrganization());
SaveCloudServer.ToPlasticGuiConfigFile(
cloudServer, GetGluonConfigFileToSaveOrganization());
KnownServers.ServersFromCloud.InitializeForWindows(
PlasticGuiConfig.Get().Configuration.DefaultCloudServer);
CloudEditionWelcome.WriteToTokensConf(
cloudServer, username, accessToken);
SetupUnityEditionToken.CreateCloudEditionTokenIfNeeded();
if (sAutoLogin)
{
ClientConfigData clientConfigData = ConfigurationChecker.GetClientConfigData();
clientConfigData.WorkspaceServer = cloudServer;
clientConfigData.WorkingMode = SEIDWorkingMode.SSOWorkingMode.ToString();
clientConfigData.SecurityConfig = username;
ClientConfig.Get().Save(clientConfigData);
GetWindow<PlasticWindow>().GetWelcomeView().autoLoginState = AutoLogin.State.OrganizationChoosed;
}
}
internal void ReplaceRootPanel(VisualElement panel)
{
rootVisualElement.Clear();
rootVisualElement.Add(panel);
}
void OnDestroy()
{
Dispose();
if (mWelcomeView != null)
mWelcomeView.OnUserClosedConfigurationWindow();
}
void Dispose()
{
if (mSignInPanel != null)
mSignInPanel.Dispose();
if (mOrganizationPanel != null)
mOrganizationPanel.Dispose();
}
void OAuthSignIn.INotify.SuccessForConfigure(
List<string> organizations,
bool canCreateAnOrganization,
string userName,
string accessToken)
{
ShowOrganizationPanel(
GetWindowTitle(),
organizations);
Focus();
mUserName = userName;
mAccessToken = accessToken;
}
internal void ShowOrganizationPanel(
string title,
List<string> organizations)
{
mOrganizationPanel = new OrganizationPanel(
this,
sRestApi,
title,
organizations);
ReplaceRootPanel(mOrganizationPanel);
}
void OAuthSignIn.INotify.SuccessForSSO(string organization)
{
// empty implementation
}
void OAuthSignIn.INotify.SuccessForProfile(string email)
{
// empty implementation
}
void OAuthSignIn.INotify.SuccessForHomeView(string homeView)
{
// empty implementation
}
void OAuthSignIn.INotify.SuccessForCredentials(
string email,
string accessToken)
{
// empty implementation
}
void OAuthSignIn.INotify.Cancel(string errorMessage)
{
Focus();
}
void IWelcomeWindowNotify.SuccessForConfigure(
List<string> organizations)
{
ShowOrganizationPanel(
GetWindowTitle(),
organizations);
}
internal void FillUserAndToken(
string userName,
string accessToken)
{
mUserName = userName;
mAccessToken = accessToken;
}
internal void ShowOrganizationPanelFromAutoLogin(
List<string> organizations)
{
ShowOrganizationPanel(
GetWindowTitle(),
organizations);
}
void IWelcomeWindowNotify.Back()
{
rootVisualElement.Clear();
rootVisualElement.Add(mSignInPanel);
}
internal string GetWindowTitle()
{
return PlasticLocalization.Name.SignInToUnityVCS.GetString();
}
internal static string GetPlasticConfigFileToSaveOrganization()
{
if (PlatformIdentifier.IsMac())
{
return "macgui.conf";
}
return "plasticgui.conf";
}
internal static string GetGluonConfigFileToSaveOrganization()
{
if (PlatformIdentifier.IsMac())
{
return "gluon.conf";
}
return "gameui.conf";
}
void BuildComponents()
{
VisualElement root = rootVisualElement;
root.Clear();
mSignInPanel = new SignInPanel(
this,
sRestApi,
sCmConnection);
titleContent = new GUIContent(GetWindowTitle());
root.Add(mSignInPanel);
if (sAutoLogin)
mSignInPanel.SignInWithUnityIdButtonAutoLogin();
}
OrganizationPanel mOrganizationPanel;
SignInPanel mSignInPanel;
WelcomeView mWelcomeView;
string mUserName;
string mAccessToken;
static IPlasticWebRestApi sRestApi;
static CmConnection sCmConnection;
static bool sAutoLogin = false;
}
}

View File

@ -0,0 +1,232 @@
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEditor.UIElements;
using UnityEngine.UIElements;
using Codice.Client.Common.Threading;
using PlasticGui;
using PlasticGui.WebApi;
using PlasticGui.WebApi.Responses;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.UIElements;
namespace Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome
{
internal class OrganizationPanel : VisualElement
{
internal OrganizationPanel(
CloudEditionWelcomeWindow parentWindow,
IPlasticWebRestApi restApi,
string title,
List<string> organizations)
{
mParentWindow = parentWindow;
mRestApi = restApi;
InitializeLayoutAndStyles();
BuildComponents(title, organizations);
EditorWindowFocus.OnApplicationActivated += OnEditorActivated;
}
internal void Dispose()
{
EditorWindowFocus.OnApplicationActivated -= OnEditorActivated;
mParentWindow.CancelJoinOrganization();
if (mJoinSingleOrganizationButton != null)
mJoinSingleOrganizationButton.clicked -= JoinOrganizationButton_clicked;
if (mJoinMultipleOrganizationsButton != null)
mJoinMultipleOrganizationsButton.clicked -= JoinOrganizationButton_clicked;
if (mOpenUnityDashboardButton != null)
mOpenUnityDashboardButton.clicked -= OpenUnityDashboardButton_clicked;
}
void OnEditorActivated()
{
if (!mReloadOrganizationsNeeded)
return;
mReloadOrganizationsNeeded = false;
mProgressControls.ShowProgress(PlasticLocalization.Name.LoadingOrganizations.GetString());
OrganizationsResponse organizationResponse = null;
IThreadWaiter waiter = ThreadWaiter.GetWaiter();
waiter.Execute(
/*threadOperationDelegate*/ delegate
{
organizationResponse = mRestApi.GetCloudServers();
},
/*afterOperationDelegate*/ delegate
{
mProgressControls.HideProgress();
if (waiter.Exception != null)
{
mProgressControls.ShowError(PlasticLocalization.Name.UnexpectedError.GetString());
ExceptionsHandler.LogException(typeof(OrganizationPanel).Name, waiter.Exception);
return;
}
if (organizationResponse.Error != null)
{
mReloadOrganizationsNeeded = true;
mProgressControls.ShowError(organizationResponse.Error.Message);
return;
}
ProcessOrganizations(organizationResponse.CloudServers);
});
}
void ProcessOrganizations(List<string> organizations)
{
this.Query<VisualElement>("noOrganization").Collapse();
this.Query<VisualElement>("joinSingleOrganization").Collapse();
this.Query<VisualElement>("joinMultipleOrganizations").Collapse();
if (organizations.Count == 0)
{
mReloadOrganizationsNeeded = true;
BuildNoOrganizationSection();
mOpenUnityDashboardButton = this.Q<Button>("openUnityDashboardButton");
mOpenUnityDashboardButton.clicked += OpenUnityDashboardButton_clicked;
return;
}
mReloadOrganizationsNeeded = false;
if (organizations.Count == 1)
{
BuildSingleOrganizationSection(organizations.First());
mJoinSingleOrganizationButton = this.Q<Button>("joinSingleOrganizationButton");
mJoinSingleOrganizationButton.clicked += JoinOrganizationButton_clicked;
return;
}
BuildMultipleOrganizationsSection(organizations);
mJoinMultipleOrganizationsButton = this.Q<Button>("joinMultipleOrganizationsButton");
mJoinMultipleOrganizationsButton.clicked += JoinOrganizationButton_clicked;
mOrganizationToJoin = organizations.First();
}
void InitializeLayoutAndStyles()
{
this.LoadLayout(typeof(OrganizationPanel).Name);
this.LoadStyle(typeof(OrganizationPanel).Name);
}
void JoinOrganizationButton_clicked()
{
mParentWindow.JoinOrganizationAndWelcomePage(mOrganizationToJoin);
// TODO: Closing the window for now. Need to connect this event to the main on boarding
// workflow.
mParentWindow.Close();
}
void OpenUnityDashboardButton_clicked()
{
Application.OpenURL(UnityUrl.UnityDashboard.Get());
}
void BuildComponents(string title, List<string> organizations)
{
mParentWindow.titleContent = new UnityEngine.GUIContent(title);
mProgressControls = new ProgressControlsForDialogs(null);
mProgressContainer = this.Q<VisualElement>("progressContainer");
mProgressContainer.Add((VisualElement)mProgressControls);
ProcessOrganizations(organizations);
}
void BuildSingleOrganizationSection(string organizationName)
{
this.SetControlText<Label>("confirmationMessage",
PlasticLocalization.Name.JoinOrganizationTitle);
mOrganizationToJoin = organizationName;
this.Query<VisualElement>("joinSingleOrganization").Show();
this.SetControlText<Label>("joinSingleOrganizationLabel",
PlasticLocalization.Name.YouBelongToOrganization, organizationName);
this.SetControlText<Button>("joinSingleOrganizationButton",
PlasticLocalization.Name.JoinButton);
}
void BuildMultipleOrganizationsSection(List<string> organizationNames)
{
this.SetControlText<Label>("confirmationMessage",
PlasticLocalization.Name.JoinOrganizationTitle);
this.Query<VisualElement>("joinMultipleOrganizations").Show();
this.SetControlText<Label>("joinMultipleOrganizationsLabel",
PlasticLocalization.Name.YouBelongToSeveralOrganizations);
VisualElement organizationDropdown = this.Query<VisualElement>("organizationDropdown");
ToolbarMenu toolbarMenu = new ToolbarMenu
{
text = organizationNames.FirstOrDefault(),
};
foreach (string name in organizationNames)
{
toolbarMenu.menu.AppendAction(name, x =>
{
toolbarMenu.text = name;
mOrganizationToJoin = name;
}, DropdownMenuAction.AlwaysEnabled);
organizationDropdown.Add(toolbarMenu);
}
this.SetControlText<Button>("joinMultipleOrganizationsButton",
PlasticLocalization.Name.JoinButton);
}
void BuildNoOrganizationSection()
{
this.SetControlText<Label>("confirmationMessage",
PlasticLocalization.Name.CreateOrganizationTitle);
this.Query<VisualElement>("noOrganization").Show();
this.SetControlImage("iconUnity",
Images.Name.ButtonSsoSignInUnity);
this.SetControlText<Label>("noOrganizationLabel",
PlasticLocalization.Name.ClickButtonBelowToCreateOrg);
this.SetControlText<Button>("openUnityDashboardButton",
PlasticLocalization.Name.MainSidebarOpenUnityDashboardItem);
}
string mOrganizationToJoin = "";
bool mReloadOrganizationsNeeded;
Button mJoinSingleOrganizationButton;
Button mJoinMultipleOrganizationsButton;
Button mOpenUnityDashboardButton;
VisualElement mProgressContainer;
IProgressControls mProgressControls;
readonly CloudEditionWelcomeWindow mParentWindow;
readonly IPlasticWebRestApi mRestApi;
}
}

View File

@ -0,0 +1,166 @@
using System;
using UnityEngine;
using UnityEngine.UIElements;
using Codice.Client.Common;
using Codice.Client.Common.OAuth;
using PlasticGui;
using PlasticGui.WebApi;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.UIElements;
using PlasticGui.Configuration.CloudEdition.Welcome;
using PlasticGui.Configuration.OAuth;
namespace Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome
{
internal class SignInPanel : VisualElement
{
internal SignInPanel(
CloudEditionWelcomeWindow parentWindow,
IPlasticWebRestApi restApi,
CmConnection cmConnection)
{
mParentWindow = parentWindow;
mRestApi = restApi;
mCmConnection = cmConnection;
InitializeLayoutAndStyles();
BuildComponents();
}
internal void Dispose()
{
mSignInWithUnityIdButton.clicked -= SignInWithUnityIdButton_Clicked;
mSignInWithEmailButton.clicked -= SignInWithEmailButton_Clicked;
mPrivacyPolicyStatementButton.clicked -= PrivacyPolicyStatementButton_Clicked;
mSignUpButton.clicked -= SignUpButton_Clicked;
if (mSignInWithEmailPanel != null)
mSignInWithEmailPanel.Dispose();
if (mWaitingSignInPanel != null)
mWaitingSignInPanel.Dispose();
}
void SignInWithEmailButton_Clicked()
{
mSignInWithEmailPanel = new SignInWithEmailPanel(
mParentWindow,
mParentWindow,
mRestApi);
mParentWindow.ReplaceRootPanel(mSignInWithEmailPanel);
}
void SignUpButton_Clicked()
{
Application.OpenURL(UnityUrl.DevOps.GetSignUp());
}
internal void SignInWithUnityIdButton_Clicked()
{
mWaitingSignInPanel = new WaitingSignInPanel(
mParentWindow,
mParentWindow,
mRestApi,
mCmConnection);
mParentWindow.ReplaceRootPanel(mWaitingSignInPanel);
Guid state = Guid.NewGuid();
mWaitingSignInPanel.OAuthSignInForConfigure(
GetCloudSsoProviders.BuildAuthInfoForUnityId(string.Empty, state).SignInUrl,
state,
new GetCloudSsoToken(mRestApi));
}
internal void SignInWithUnityIdButtonAutoLogin()
{
mWaitingSignInPanel = new WaitingSignInPanel(
mParentWindow,
mParentWindow,
mRestApi,
mCmConnection);
mParentWindow.ReplaceRootPanel(mWaitingSignInPanel);
}
void PrivacyPolicyStatementButton_Clicked()
{
Application.OpenURL(SignUp.PRIVACY_POLICY_URL);
}
void BuildComponents()
{
BuildSignUpArea();
BuildSignInUnityIdArea();
BuildSignInEmailArea();
BuildPrivatePolicyArea();
}
void BuildPrivatePolicyArea()
{
this.SetControlText<Label>(
"privacyStatementText",
PlasticLocalization.Name.PrivacyStatementText,
PlasticLocalization.GetString(PlasticLocalization.Name.PrivacyStatement));
mPrivacyPolicyStatementButton = this.Query<Button>("privacyStatement");
mPrivacyPolicyStatementButton.text = PlasticLocalization.Name.PrivacyStatement.GetString();
mPrivacyPolicyStatementButton.clicked += PrivacyPolicyStatementButton_Clicked;
}
void BuildSignInEmailArea()
{
this.SetControlImage(
"iconEmail",
Images.Name.ButtonSsoSignInEmail);
mSignInWithEmailButton = this.Query<Button>("emailButton");
mSignInWithEmailButton.text = PlasticLocalization.Name.SignInWithEmail.GetString();
mSignInWithEmailButton.clicked += SignInWithEmailButton_Clicked;
}
void BuildSignInUnityIdArea()
{
this.SetControlImage(
"iconUnity",
Images.Name.ButtonSsoSignInUnity);
mSignInWithUnityIdButton = this.Query<Button>("unityIDButton");
mSignInWithUnityIdButton.text = PlasticLocalization.Name.SignInWithUnityID.GetString();
mSignInWithUnityIdButton.clicked += SignInWithUnityIdButton_Clicked;
}
void BuildSignUpArea()
{
Label signUpLabel = this.Query<Label>("signUpLabel");
signUpLabel.text = PlasticLocalization.Name.LoginOrSignUp.GetString();
mSignUpButton = this.Query<Button>("signUpButton");
mSignUpButton.text = PlasticLocalization.Name.SignUpButton.GetString();
mSignUpButton.clicked += SignUpButton_Clicked;
}
void InitializeLayoutAndStyles()
{
AddToClassList("grow");
this.LoadLayout(typeof(SignInPanel).Name);
this.LoadStyle(typeof(SignInPanel).Name);
}
SignInWithEmailPanel mSignInWithEmailPanel;
WaitingSignInPanel mWaitingSignInPanel;
Button mSignInWithUnityIdButton;
Button mSignInWithEmailButton;
Button mPrivacyPolicyStatementButton;
Button mSignUpButton;
readonly CloudEditionWelcomeWindow mParentWindow;
readonly IPlasticWebRestApi mRestApi;
readonly CmConnection mCmConnection;
}
}

View File

@ -0,0 +1,203 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
using PlasticGui;
using PlasticGui.Configuration.CloudEdition.Welcome;
using PlasticGui.Configuration.CloudEdition;
using PlasticGui.WebApi;
using Unity.PlasticSCM.Editor.UI.UIElements;
namespace Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome
{
internal class SignInWithEmailPanel :
VisualElement,
Login.INotify
{
internal SignInWithEmailPanel(
CloudEditionWelcomeWindow parentWindow,
IWelcomeWindowNotify notify,
IPlasticWebRestApi restApi)
{
mParentWindow = parentWindow;
mNotify = notify;
mRestApi = restApi;
InitializeLayoutAndStyles();
BuildComponents();
}
internal void Dispose()
{
mSignInButton.clicked -= SignInButton_Clicked;
mBackButton.clicked -= BackButton_Clicked;
mSignUpButton.clicked -= SignUpButton_Clicked;
}
void Login.INotify.SuccessForConfigure(
List<string> organizations,
bool canCreateAnOrganization,
string userName,
string password)
{
mNotify.SuccessForConfigure(
organizations);
}
void Login.INotify.SuccessForSSO(
string organization)
{
// Do nothing
}
void Login.INotify.SuccessForProfile(
string userName)
{
// Do nothing
}
void Login.INotify.SuccessForCredentials(string userName, string password)
{
// Do nothing
}
void Login.INotify.SuccessForHomeView(string userName)
{
// Do nothing
}
void Login.INotify.ValidationFailed(
Login.ValidationResult validationResult)
{
if (validationResult.UserError != null)
{
mEmailNotificationLabel.text = validationResult.UserError;
}
if (validationResult.PasswordError != null)
{
mPasswordNotificationLabel.text = validationResult.PasswordError;
}
}
void Login.INotify.SignUpNeeded(
Login.Data loginData)
{
ShowSignUpNeeded();
}
void ShowSignUpNeeded()
{
mSignUpNeededNotificationContainer.Show();
}
void HideSignUpNeeded()
{
mSignUpNeededNotificationContainer.Collapse();
}
void Login.INotify.Error(
string message)
{
HideSignUpNeeded();
mProgressControls.ShowError(message);
}
void CleanNotificationLabels()
{
mEmailNotificationLabel.text = string.Empty;
mPasswordNotificationLabel.text = string.Empty;
HideSignUpNeeded();
}
void SignInButton_Clicked()
{
CleanNotificationLabels();
Login.Run(
mRestApi,
new SaveCloudEditionCreds(),
mEmailField.text,
mPasswordField.text,
string.Empty,
string.Empty,
Login.Mode.Configure,
mProgressControls,
this);
}
void BackButton_Clicked()
{
mNotify.Back();
}
void InitializeLayoutAndStyles()
{
this.LoadLayout(typeof(SignInWithEmailPanel).Name);
this.LoadStyle(typeof(SignInWithEmailPanel).Name);
}
void SignUpButton_Clicked()
{
Application.OpenURL(UnityUrl.DevOps.GetSignUp());
}
void BuildComponents()
{
mEmailField = this.Q<TextField>("email");
mPasswordField = this.Q<TextField>("password");
mEmailNotificationLabel = this.Q<Label>("emailNotification");
mPasswordNotificationLabel = this.Q<Label>("passwordNotification");
mSignInButton = this.Q<Button>("signIn");
mBackButton = this.Q<Button>("back");
mSignUpButton = this.Q<Button>("signUpButton");
mProgressContainer = this.Q<VisualElement>("progressContainer");
mSignUpNeededNotificationContainer = this.Q<VisualElement>("signUpNeededNotificationContainer");
mSignInButton.clicked += SignInButton_Clicked;
mBackButton.clicked += BackButton_Clicked;
mSignUpButton.clicked += SignUpButton_Clicked;
mEmailField.FocusOnceLoaded();
mProgressControls = new ProgressControlsForDialogs(new VisualElement[] { mSignInButton });
mProgressContainer.Add((VisualElement)mProgressControls);
this.SetControlText<Label>("signInLabel",
PlasticLocalization.Name.SignInWithEmail);
this.SetControlLabel<TextField>("email",
PlasticLocalization.Name.Email);
this.SetControlLabel<TextField>("password",
PlasticLocalization.Name.Password);
this.SetControlText<Button>("signIn",
PlasticLocalization.Name.SignIn);
this.SetControlText<Button>("back",
PlasticLocalization.Name.BackButton);
this.SetControlText<Label>("signUpNeededNotificationLabel",
PlasticLocalization.Name.SignUpNeededNoArgs);
this.SetControlText<Button>("signUpButton",
PlasticLocalization.Name.SignUp);
}
TextField mEmailField;
TextField mPasswordField;
Label mEmailNotificationLabel;
Label mPasswordNotificationLabel;
Button mSignInButton;
Button mBackButton;
Button mSignUpButton;
VisualElement mProgressContainer;
VisualElement mSignUpNeededNotificationContainer;
IProgressControls mProgressControls;
readonly CloudEditionWelcomeWindow mParentWindow;
readonly IWelcomeWindowNotify mNotify;
readonly IPlasticWebRestApi mRestApi;
}
}

View File

@ -0,0 +1,110 @@
using System;
using Codice.Client.Common;
using Codice.Client.Common.OAuth;
using PlasticGui;
using PlasticGui.Configuration.OAuth;
using PlasticGui.WebApi;
using Unity.PlasticSCM.Editor.UI.UIElements;
using UnityEngine.UIElements;
namespace Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome
{
internal class WaitingSignInPanel : VisualElement
{
internal WaitingSignInPanel(
IWelcomeWindowNotify parentNotify,
OAuthSignIn.INotify notify,
IPlasticWebRestApi restApi,
CmConnection cmConnection)
{
mParentNotify = parentNotify;
mNotify = notify;
mRestApi = restApi;
mCmConnection = cmConnection;
InitializeLayoutAndStyles();
BuildComponents();
}
internal void OAuthSignInForConfigure(
Uri signInUrl,
Guid state,
IGetOauthToken getToken)
{
mSignIn = new OAuthSignIn();
mSignIn.ForConfigure(
signInUrl,
state,
mProgressControls,
mNotify,
mCmConnection,
getToken,
mRestApi);
ShowWaitingSpinner();
}
internal void Dispose()
{
mCancelButton.clicked -= CancelButton_Clicked;
}
void CancelButton_Clicked()
{
mSignIn.Cancel();
mParentNotify.Back();
}
void BuildComponents()
{
this.SetControlText<Label>("signInToPlasticSCM",
PlasticLocalization.Name.SignInToUnityVCS);
this.SetControlText<Label>("completeSignInOnBrowser",
PlasticLocalization.Name.CompleteSignInOnBrowser);
mProgressContainer = this.Q<VisualElement>("progressContainer");
mProgressControls = new UI.Progress.ProgressControlsForDialogs();
mCancelButton = this.Query<Button>("cancelButton");
mCancelButton.text = PlasticLocalization.GetString(
PlasticLocalization.Name.CancelButton);
mCancelButton.clicked += CancelButton_Clicked;
}
void InitializeLayoutAndStyles()
{
this.LoadLayout(typeof(WaitingSignInPanel).Name);
this.LoadStyle(typeof(WaitingSignInPanel).Name);
}
void ShowWaitingSpinner()
{
var spinner = new LoadingSpinner();
mProgressContainer.Add(spinner);
spinner.Start();
var checkinMessageLabel = new Label(mProgressControls.ProgressData.ProgressMessage);
checkinMessageLabel.style.paddingLeft = 20;
mProgressContainer.Add(checkinMessageLabel);
}
Button mCancelButton;
VisualElement mProgressContainer;
OAuthSignIn mSignIn;
UI.Progress.ProgressControlsForDialogs mProgressControls;
readonly IPlasticWebRestApi mRestApi;
readonly CmConnection mCmConnection;
readonly OAuthSignIn.INotify mNotify;
readonly IWelcomeWindowNotify mParentNotify;
}
}

View File

@ -0,0 +1,33 @@
using System.Collections.Generic;
using System.IO;
using Codice.Client.Commands.Mount;
using Codice.Client.Commands.WkTree;
using Codice.Client.Common;
using Codice.Client.Common.GameUI;
using Codice.CM.Common;
using Codice.CM.WorkspaceServer.DataStore.Configuration;
namespace Unity.PlasticSCM.Editor.Configuration
{
internal static class ConfigurePartialWorkspace
{
internal static void AsFullyChecked(WorkspaceInfo wkInfo)
{
string rootPath = WorkspacePath.GetWorkspacePathFromCmPath(
wkInfo.ClientPath, "/", Path.DirectorySeparatorChar);
WorkspaceTreeNode rootWkNode = CmConnection.Get().GetWorkspaceTreeHandler().
WkGetWorkspaceTreeNode(rootPath);
FullyCheckedDirectory rootDirectory = new FullyCheckedDirectory();
rootDirectory.MountId = MountPointId.WORKSPACE_ROOT;
rootDirectory.ItemId = rootWkNode.RevInfo.ItemId;
List<FullyCheckedDirectory> directoryList = new List<FullyCheckedDirectory>();
directoryList.Add(rootDirectory);
FullyCheckedDirectoriesStorage.Save(wkInfo, directoryList);
}
}
}

View File

@ -0,0 +1,160 @@
using UnityEngine;
using UnityEditor;
using PlasticGui;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.Progress;
using Codice.CM.Common;
using Codice.Client.Common.Connection;
namespace Unity.PlasticSCM.Editor.Configuration
{
internal class CredentialsDialog : PlasticDialog
{
protected override Rect DefaultRect
{
get
{
var baseRect = base.DefaultRect;
return new Rect(baseRect.x, baseRect.y, 525, 250);
}
}
internal static AskCredentialsToUser.DialogData RequestCredentials(
string server,
SEIDWorkingMode seidWorkingMode,
EditorWindow parentWindow)
{
CredentialsDialog dialog = Create(
server, seidWorkingMode, new ProgressControlsForDialogs());
ResponseType dialogResult = dialog.RunModal(parentWindow);
return dialog.BuildCredentialsDialogData(dialogResult);
}
protected override void OnModalGUI()
{
Title(PlasticLocalization.GetString(
PlasticLocalization.Name.CredentialsDialogTitle));
Paragraph(PlasticLocalization.GetString(
PlasticLocalization.Name.CredentialsDialogExplanation, mServer));
GUILayout.Space(5);
DoEntriesArea();
GUILayout.Space(10);
DrawProgressForDialogs.For(
mProgressControls.ProgressData);
GUILayout.Space(10);
DoButtonsArea();
}
protected override string GetTitle()
{
return PlasticLocalization.GetString(
PlasticLocalization.Name.CredentialsDialogTitle);
}
AskCredentialsToUser.DialogData BuildCredentialsDialogData(
ResponseType dialogResult)
{
return new AskCredentialsToUser.DialogData(
dialogResult == ResponseType.Ok,
mUser, mPassword, mSaveProfile, mSeidWorkingMode);
}
void DoEntriesArea()
{
mUser = TextEntry(PlasticLocalization.GetString(
PlasticLocalization.Name.UserName), mUser,
ENTRY_WIDTH, ENTRY_X);
GUILayout.Space(5);
mPassword = PasswordEntry(PlasticLocalization.GetString(
PlasticLocalization.Name.Password), mPassword,
ENTRY_WIDTH, ENTRY_X);
GUILayout.Space(5);
mSaveProfile = ToggleEntry(PlasticLocalization.GetString(
PlasticLocalization.Name.RememberCredentialsAsProfile),
mSaveProfile, ENTRY_WIDTH, ENTRY_X);
}
void DoButtonsArea()
{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (Application.platform == RuntimePlatform.WindowsEditor)
{
DoOkButton();
DoCancelButton();
return;
}
DoCancelButton();
DoOkButton();
}
}
void DoOkButton()
{
if (!AcceptButton(PlasticLocalization.GetString(
PlasticLocalization.Name.OkButton)))
return;
OkButtonWithValidationAction();
}
void DoCancelButton()
{
if (!NormalButton(PlasticLocalization.GetString(
PlasticLocalization.Name.CancelButton)))
return;
CancelButtonAction();
}
void OkButtonWithValidationAction()
{
CredentialsDialogValidation.AsyncValidation(
BuildCredentialsDialogData(ResponseType.Ok), this, mProgressControls);
}
static CredentialsDialog Create(
string server,
SEIDWorkingMode seidWorkingMode,
ProgressControlsForDialogs progressControls)
{
var instance = CreateInstance<CredentialsDialog>();
instance.mServer = server;
instance.mSeidWorkingMode = seidWorkingMode;
instance.mProgressControls = progressControls;
instance.mEnterKeyAction = instance.OkButtonWithValidationAction;
instance.mEscapeKeyAction = instance.CancelButtonAction;
return instance;
}
string mUser;
string mPassword = string.Empty;
ProgressControlsForDialogs mProgressControls;
bool mSaveProfile;
string mServer;
SEIDWorkingMode mSeidWorkingMode;
const float ENTRY_WIDTH = 345f;
const float ENTRY_X = 150f;
}
}

View File

@ -0,0 +1,127 @@
using System;
using UnityEditor;
using Codice.CM.Common;
using System.Threading.Tasks;
using Codice.Client.Common;
using Codice.Client.Common.Connection;
using PlasticGui;
using Unity.PlasticSCM.Editor.UI;
using Codice.Client.Common.Threading;
using Unity.PlasticSCM.Editor.WebApi;
namespace Unity.PlasticSCM.Editor.Configuration
{
internal class CredentialsUiImpl : AskCredentialsToUser.IGui
{
AskCredentialsToUser.DialogData AskCredentialsToUser.IGui.AskUserForCredentials(string servername, SEIDWorkingMode seidWorkingMode)
{
AskCredentialsToUser.DialogData result = null;
if (!PlasticPlugin.ConnectionMonitor.IsConnected)
return result;
GUIActionRunner.RunGUIAction(delegate
{
result = CredentialsDialog.RequestCredentials(
servername, seidWorkingMode, ParentWindow.Get());
});
return result;
}
void AskCredentialsToUser.IGui.ShowSaveProfileErrorMessage(string message)
{
if (!PlasticPlugin.ConnectionMonitor.IsConnected)
return;
GUIActionRunner.RunGUIAction(delegate
{
GuiMessage.ShowError(string.Format(
PlasticLocalization.GetString(
PlasticLocalization.Name.CredentialsErrorSavingProfile),
message));
});
}
AskCredentialsToUser.DialogData AskCredentialsToUser.IGui.AskUserForOidcCredentials(
string server)
{
throw new NotImplementedException("OIDC authentication not supported yet.");
}
AskCredentialsToUser.DialogData AskCredentialsToUser.IGui.AskUserForSsoCredentials(
string cloudServer)
{
AskCredentialsToUser.DialogData result = null;
if (!PlasticPlugin.ConnectionMonitor.IsConnected)
return result;
GUIActionRunner.RunGUIAction(delegate
{
result = RunSSOCredentialsRequest(
cloudServer, CloudProjectSettings.accessToken);
});
return result;
}
AskCredentialsToUser.DialogData RunSSOCredentialsRequest(
string cloudServer,
string unityAccessToken)
{
if (string.IsNullOrEmpty(unityAccessToken))
{
return SSOCredentialsDialog.RequestCredentials(
cloudServer, ParentWindow.Get());
}
TokenExchangeResponse tokenExchangeResponse =
WaitUntilTokenExchange(unityAccessToken);
// There is no internet connection, so no way to get credentials
if (tokenExchangeResponse == null)
{
return new AskCredentialsToUser.DialogData(
false, null, null, false,
SEIDWorkingMode.SSOWorkingMode);
}
if (tokenExchangeResponse.Error == null)
{
return new AskCredentialsToUser.DialogData(
true,
tokenExchangeResponse.User,
tokenExchangeResponse.AccessToken,
false,
SEIDWorkingMode.SSOWorkingMode);
}
return SSOCredentialsDialog.RequestCredentials(
cloudServer, ParentWindow.Get());
}
static TokenExchangeResponse WaitUntilTokenExchange(
string unityAccessToken)
{
TokenExchangeResponse result = null;
Task.Run(() =>
{
try
{
result = WebRestApiClient.PlasticScm.
TokenExchange(unityAccessToken);
}
catch (Exception ex)
{
ExceptionsHandler.LogException(
"CredentialsUiImpl", ex);
}
}).Wait();
return result;
}
}
}

View File

@ -0,0 +1,196 @@
using UnityEditor;
using UnityEngine;
using Codice.Utils;
using PlasticGui;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.Configuration
{
internal class EncryptionConfigurationDialog : PlasticDialog
{
protected override Rect DefaultRect
{
get
{
var baseRect = base.DefaultRect;
return new Rect(baseRect.x, baseRect.y, 650, 425);
}
}
internal static EncryptionConfigurationDialogData RequestEncryptionPassword(
string server,
EditorWindow parentWindow)
{
EncryptionConfigurationDialog dialog = Create(server);
ResponseType dialogResult = dialog.RunModal(parentWindow);
EncryptionConfigurationDialogData result =
dialog.BuildEncryptionConfigurationData();
result.Result = dialogResult == ResponseType.Ok;
return result;
}
protected override void OnModalGUI()
{
Title(PlasticLocalization.GetString(
PlasticLocalization.Name.EncryptionConfiguration));
GUILayout.Space(20);
Paragraph(PlasticLocalization.GetString(
PlasticLocalization.Name.EncryptionConfigurationExplanation, mServer));
DoPasswordArea();
Paragraph(PlasticLocalization.GetString(
PlasticLocalization.Name.EncryptionConfigurationRemarks, mServer));
GUILayout.Space(10);
DoNotificationArea();
GUILayout.Space(10);
DoButtonsArea();
}
protected override string GetTitle()
{
return PlasticLocalization.GetString(
PlasticLocalization.Name.EncryptionConfiguration);
}
EncryptionConfigurationDialogData BuildEncryptionConfigurationData()
{
return new EncryptionConfigurationDialogData(
CryptoServices.GetEncryptedPassword(mPassword.Trim()));
}
void DoPasswordArea()
{
Paragraph(PlasticLocalization.GetString(
PlasticLocalization.Name.EncryptionConfigurationEnterPassword));
GUILayout.Space(5);
mPassword = PasswordEntry(PlasticLocalization.GetString(
PlasticLocalization.Name.Password), mPassword,
PASSWORD_TEXT_WIDTH, PASSWORD_TEXT_X);
GUILayout.Space(5);
mRetypePassword = PasswordEntry(PlasticLocalization.GetString(
PlasticLocalization.Name.RetypePassword), mRetypePassword,
PASSWORD_TEXT_WIDTH, PASSWORD_TEXT_X);
GUILayout.Space(18f);
}
void DoNotificationArea()
{
if (string.IsNullOrEmpty(mErrorMessage))
return;
var rect = GUILayoutUtility.GetRect(
GUILayoutUtility.GetLastRect().width, 30);
EditorGUI.HelpBox(rect, mErrorMessage, MessageType.Error);
}
void DoButtonsArea()
{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (Application.platform == RuntimePlatform.WindowsEditor)
{
DoOkButton();
DoCancelButton();
return;
}
DoCancelButton();
DoOkButton();
}
}
void DoOkButton()
{
if (!AcceptButton(PlasticLocalization.GetString(
PlasticLocalization.Name.OkButton)))
return;
OkButtonWithValidationAction();
}
void DoCancelButton()
{
if (!NormalButton(PlasticLocalization.GetString(
PlasticLocalization.Name.CancelButton)))
return;
CancelButtonAction();
}
void OkButtonWithValidationAction()
{
if (IsValidPassword(
mPassword.Trim(), mRetypePassword.Trim(),
out mErrorMessage))
{
mErrorMessage = string.Empty;
OkButtonAction();
return;
}
mPassword = string.Empty;
mRetypePassword = string.Empty;
}
static bool IsValidPassword(
string password, string retypePassword,
out string errorMessage)
{
errorMessage = string.Empty;
if (string.IsNullOrEmpty(password))
{
errorMessage = PlasticLocalization.GetString(
PlasticLocalization.Name.InvalidEmptyPassword);
return false;
}
if (!password.Equals(retypePassword))
{
errorMessage = PlasticLocalization.GetString(
PlasticLocalization.Name.PasswordDoesntMatch);
return false;
}
return true;
}
static EncryptionConfigurationDialog Create(string server)
{
var instance = CreateInstance<EncryptionConfigurationDialog>();
instance.mServer = server;
instance.mEnterKeyAction = instance.OkButtonWithValidationAction;
instance.mEscapeKeyAction = instance.CancelButtonAction;
return instance;
}
string mPassword = string.Empty;
string mRetypePassword = string.Empty;
string mErrorMessage = string.Empty;
string mServer = string.Empty;
const float PASSWORD_TEXT_WIDTH = 250f;
const float PASSWORD_TEXT_X = 200f;
}
}

View File

@ -0,0 +1,36 @@
using UnityEditor;
using Codice.Client.Common.Encryption;
using PlasticGui;
using Unity.PlasticSCM.Editor.UI;
namespace Unity.PlasticSCM.Editor.Configuration
{
internal class MissingEncryptionPasswordPromptHandler :
ClientEncryptionServiceProvider.IEncryptioPasswordProvider
{
string ClientEncryptionServiceProvider.IEncryptioPasswordProvider
.GetEncryptionEncryptedPassword(string server)
{
string result = null;
GUIActionRunner.RunGUIAction(delegate
{
result = AskForEncryptionPassword(server);
});
return result;
}
string AskForEncryptionPassword(string server)
{
EncryptionConfigurationDialogData dialogData =
EncryptionConfigurationDialog.RequestEncryptionPassword(server, ParentWindow.Get());
if (!dialogData.Result)
return null;
return dialogData.EncryptedPassword;
}
}
}

View File

@ -0,0 +1,296 @@
using System;
using UnityEngine;
using UnityEditor;
using PlasticGui;
using Unity.PlasticSCM.Editor.UI;
using Unity.PlasticSCM.Editor.UI.Progress;
using Codice.CM.Common;
using Codice.Client.Common.OAuth;
using Codice.Client.Common.Connection;
using Unity.PlasticSCM.Editor.Configuration.CloudEdition.Welcome;
using PlasticGui.Configuration.CloudEdition.Welcome;
using PlasticGui.Configuration.OAuth;
using System.Collections.Generic;
using PlasticGui.WebApi.Responses;
using PlasticGui.Configuration.CloudEdition;
namespace Unity.PlasticSCM.Editor.Configuration
{
internal class SSOCredentialsDialog : PlasticDialog, OAuthSignIn.INotify, Login.INotify
{
protected override Rect DefaultRect
{
get
{
var baseRect = base.DefaultRect;
return new Rect(baseRect.x, baseRect.y, 525, 450);
}
}
internal static AskCredentialsToUser.DialogData RequestCredentials(
string cloudServer,
EditorWindow parentWindow)
{
SSOCredentialsDialog dialog = Create(
cloudServer, new ProgressControlsForDialogs());
ResponseType dialogResult = dialog.RunModal(parentWindow);
return dialog.BuildCredentialsDialogData(dialogResult);
}
protected override void OnModalGUI()
{
Title(PlasticLocalization.GetString(
PlasticLocalization.Name.CredentialsDialogTitle));
Paragraph(PlasticLocalization.GetString(
PlasticLocalization.Name.CredentialsDialogExplanation, mServer));
GUILayout.Space(20);
DoEntriesArea();
GUILayout.Space(10);
DrawProgressForDialogs.For(
mProgressControls.ProgressData);
GUILayout.Space(10);
DoButtonsArea();
}
protected override string GetTitle()
{
return PlasticLocalization.GetString(
PlasticLocalization.Name.CredentialsDialogTitle);
}
AskCredentialsToUser.DialogData BuildCredentialsDialogData(
ResponseType dialogResult)
{
return new AskCredentialsToUser.DialogData(
dialogResult == ResponseType.Ok,
mEmail, mPassword, false, SEIDWorkingMode.SSOWorkingMode);
}
void OAuthSignIn.INotify.SuccessForSSO(string organization)
{
OkButtonAction();
}
void OAuthSignIn.INotify.SuccessForProfile(string email)
{
OkButtonAction();
}
void OAuthSignIn.INotify.SuccessForCredentials(
string email,
string accessToken)
{
OkButtonAction();
}
void OAuthSignIn.INotify.SuccessForHomeView(string usrName)
{
}
void OAuthSignIn.INotify.Cancel(string errorMessage)
{
CancelButtonAction();
}
void OAuthSignIn.INotify.SuccessForConfigure(
List<string> organizations,
bool canCreateAnOrganization,
string userName,
string accessToken)
{
mEmail = userName;
mPassword = accessToken;
if (!organizations.Contains(mServer))
{
CancelButtonAction();
return;
}
CloudEditionWelcomeWindow.JoinCloudServer(
mServer, userName, accessToken);
GetWindow<PlasticWindow>().InitializePlastic();
OkButtonAction();
}
void DoButtonsArea()
{
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.FlexibleSpace();
if (Application.platform == RuntimePlatform.WindowsEditor)
{
DoOkButton();
DoCancelButton();
return;
}
DoCancelButton();
DoOkButton();
}
}
internal void OAuthSignInForConfigure(Uri signInUrl, Guid state, IGetOauthToken getOauthToken)
{
OAuthSignIn mSignIn = new OAuthSignIn();
mSignIn.ForConfigure(
signInUrl,
state,
mProgressControls,
this,
GetWindow<PlasticWindow>().CmConnectionForTesting,
getOauthToken,
PlasticGui.Plastic.WebRestAPI);
}
void DoUnityIDButton()
{
if (NormalButton("Sign in with Unity ID"))
{
Guid state = Guid.NewGuid();
OAuthSignInForConfigure(
GetCloudSsoProviders.BuildAuthInfoForUnityId(string.Empty, state).SignInUrl,
state,
new GetCloudSsoToken(PlasticGui.Plastic.WebRestAPI));
}
}
void DoEntriesArea()
{
Paragraph("Sign in with Unity ID");
GUILayout.Space(5);
DoUnityIDButton();
GUILayout.Space(25);
Paragraph(" --or-- ");
Paragraph("Sign in with email");
mEmail = TextEntry(PlasticLocalization.GetString(
PlasticLocalization.Name.Email), mEmail,
ENTRY_WIDTH, ENTRY_X);
GUILayout.Space(5);
mPassword = PasswordEntry(PlasticLocalization.GetString(
PlasticLocalization.Name.Password), mPassword,
ENTRY_WIDTH, ENTRY_X);
}
void DoOkButton()
{
if (!AcceptButton(PlasticLocalization.GetString(
PlasticLocalization.Name.OkButton)))
return;
OkButtonWithValidationAction();
}
void DoCancelButton()
{
if (!NormalButton(PlasticLocalization.GetString(
PlasticLocalization.Name.CancelButton)))
return;
CancelButtonAction();
}
void OkButtonWithValidationAction()
{
Login.Run(
PlasticGui.Plastic.WebRestAPI,
new SaveCloudEditionCreds(),
mEmail,
mPassword,
string.Empty,
string.Empty,
Login.Mode.Configure,
mProgressControls,
this);
}
void Login.INotify.SuccessForConfigure(
List<string> organizations,
bool canCreateAnOrganization,
string userName,
string password)
{
OkButtonAction();
}
void Login.INotify.SuccessForSSO(
string organization)
{
OkButtonAction();
}
void Login.INotify.SuccessForCredentials(string userName, string password)
{
OkButtonAction();
}
void Login.INotify.SuccessForProfile(
string userName)
{
OkButtonAction();
}
void Login.INotify.SuccessForHomeView(string userName)
{
}
void Login.INotify.ValidationFailed(
Login.ValidationResult validationResult)
{
CancelButtonAction();
}
void Login.INotify.SignUpNeeded(
Login.Data loginData)
{
CancelButtonAction();
}
void Login.INotify.Error(
string message)
{
CancelButtonAction();
}
static SSOCredentialsDialog Create(
string server,
ProgressControlsForDialogs progressControls)
{
var instance = CreateInstance<SSOCredentialsDialog>();
instance.mServer = server;
instance.mProgressControls = progressControls;
instance.mEnterKeyAction = instance.OkButtonWithValidationAction;
instance.mEscapeKeyAction = instance.CancelButtonAction;
return instance;
}
string mEmail;
string mPassword = string.Empty;
ProgressControlsForDialogs mProgressControls;
string mServer;
const float ENTRY_WIDTH = 345f;
const float ENTRY_X = 150f;
}
}

View File

@ -0,0 +1,314 @@
using System;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using PlasticGui;
using Codice.CM.Common;
using Codice.Client.Common;
using Unity.PlasticSCM.Editor.UI.UIElements;
using PlasticGui.Configuration.TeamEdition;
using PlasticGui.Configuration;
using PlasticGui.WebApi;
using Unity.PlasticSCM.Editor.Views.Welcome;
namespace Unity.PlasticSCM.Editor.Configuration.TeamEdition
{
internal class TeamEditionConfigurationWindow : EditorWindow
{
internal static void ShowWindow(IPlasticWebRestApi restApi, WelcomeView welcomeView)
{
TeamEditionConfigurationWindow window = GetWindow<TeamEditionConfigurationWindow>();
window.mRestApi = restApi;
window.mWelcomeView = welcomeView;
window.titleContent = new GUIContent(
PlasticLocalization.GetString(PlasticLocalization.Name.WelcomeToUnityVCS));
window.minSize = window.maxSize = new Vector2(650, 300);
window.Show();
}
void OnEnable()
{
InitializeLayoutAndStyles();
BuildComponents();
}
void Dispose()
{
mConnectButton.clicked -= ConnectButton_Clicked;
mCheckConnectionButton.clicked -= CheckConnectionButton_Clicked;
mOkButton.clicked -= OkButton_Clicked;
mCancelButton.clicked -= CancelButton_Clicked;
mServerTextField.UnregisterValueChangedCallback(OnServerTextFieldChanged);
mUseSslToggle.UnregisterValueChangedCallback(OnUseSslToggleChanged);
mLoadingSpinner.Dispose();
}
void ConnectButton_Clicked()
{
ConfigurationConnectServerButtonClickEvent.Click(
server: mUserAssistant.GetProposedServer(),
HideValidation: HideValidation,
ShowError: ShowServerValidationError,
ShowProgress: ShowProgress,
HideProgress: HideProgress,
ShowNotification: ShowServerNotificationMessage,
DisableButtons: () => { mConnectButton.SetEnabled(false); },
EnableButtons: () => { mConnectButton.SetEnabled(true); },
UpdatePasswordEntries: (seidWorkingMode) =>
{
UpdatePasswordEntries(ValidateServerAndCreds.
IsPasswordRequired(seidWorkingMode));
},
NotifyWorkingMode: (mode) => { mSEIDWorkingMode = mode; },
NotifyConnectedStatus: (b) => { });
mUserTextField.SetEnabled(true);
}
void OnDestroy()
{
Dispose();
if (mWelcomeView != null)
mWelcomeView.OnUserClosedConfigurationWindow();
}
void CheckConnectionButton_Clicked()
{
ConfigurationCheckCredentialsButtonClickEvent.Click(
mSEIDWorkingMode,
mUserTextField.value,
mPasswordTextField.value,
Edition.Team,
mUserAssistant,
HideCredentialsValidationError,
ShowCheckCredentialsError,
ShowProgress,
HideProgress,
ShowNotification: ShowCredentialsNotificationMessage,
DisableButtons: () => { mCheckConnectionButton.SetEnabled(false); mConnectButton.SetEnabled(false); },
EnableButtons: () => { mCheckConnectionButton.SetEnabled(true); mConnectButton.SetEnabled(true); },
NotifyWorkingMode: (mode) => { mSEIDWorkingMode = mode; },
NotifyConnectedStatus: (status) => { },
restApi: mRestApi,
cmConnection: CmConnection.Get());
}
void CancelButton_Clicked()
{
Close();
}
void OkButton_Clicked()
{
if (!ValidateServerAndCreds.IsValidInput(
mUserAssistant.GetProposedServer(),
mUserTextField.value,
mSEIDWorkingMode,
mPasswordTextField.value,
ShowCheckCredentialsError))
{
return;
}
ConfigurationActions.SaveClientConfig(
mServerTextField.value,
mSEIDWorkingMode,
mUserTextField.value,
mPasswordTextField.value);
Close();
}
void HideCredentialsValidationError()
{
mCredentialsLabel.RemoveFromClassList("error");
mCredentialsLabel.Hide();
}
void BuildComponents()
{
VisualElement root = rootVisualElement;
root.Query<Label>("plasticConfigurationTitle").First().text =
PlasticLocalization.GetString(PlasticLocalization.Name.PlasticConfigurationTitleUnityVCS);
root.SetControlText<Label>(
"plasticConfigurationExplanation",
PlasticLocalization.Name.PlasticConfigurationExplanation);
root.SetControlText<Label>("configurationServerInfo",
PlasticLocalization.Name.PlasticSCMServerLabel);
root.SetControlText<Button>(
"connect",
PlasticLocalization.Name.Connect);
root.SetControlText<Label>("useSsl",
PlasticLocalization.Name.UseSsl);
root.SetControlText<Label>("configurationUserNameInfo",
PlasticLocalization.Name.ConfigurationUserNameInfo);
root.SetControlText<Label>("configurationCredentialsInfo",
PlasticLocalization.Name.ConfigurationCredentialsInfo);
root.SetControlText<Button>("check",
PlasticLocalization.Name.Check);
root.SetControlText<Label>("credentialsOk",
PlasticLocalization.Name.CredentialsOK);
root.SetControlText<Button>("okButton",
PlasticLocalization.Name.OkButton);
root.SetControlText<Button>("cancelButton",
PlasticLocalization.Name.CancelButton);
mSpinnerContainer = root.Query<VisualElement>("spinnerContainer").First();
mSpinnerLabel = root.Query<Label>("spinnerLabel").First();
mLoadingSpinner = new LoadingSpinner();
mSpinnerContainer.Add(mLoadingSpinner);
mSpinnerContainer.Hide();
mCheckConnectionButton = root.Query<Button>("check").First();
mCheckConnectionButton.clicked += CheckConnectionButton_Clicked;
mConnectButton = root.Query<Button>("connect").First();
mConnectButton.clicked += ConnectButton_Clicked;
mServerTextField = root.Query<TextField>("serverTextField").First();
mServerTextField.RegisterValueChangedCallback(OnServerTextFieldChanged);
mUseSslToggle = root.Query<Toggle>("useSslToogle").First();
mUseSslToggle.RegisterValueChangedCallback(OnUseSslToggleChanged);
mUserTextField = root.Query<TextField>("userTextField").First();
mUserTextField.SetEnabled(false);
mPasswordTextField = root.Query<TextField>("passwordTextField").First();
mPasswordTextField.isPasswordField = true;
mConnectedLabel = root.Query<Label>("connectedLabel").First();
mCredentialsLabel = root.Query<Label>("credentialsOk").First();
mOkButton = root.Query<Button>("okButton").First();
mOkButton.clicked += OkButton_Clicked;
mCancelButton = root.Query<Button>("cancelButton").First();
mCancelButton.clicked += CancelButton_Clicked;
}
void OnUseSslToggleChanged(ChangeEvent<bool> evt)
{
mUserAssistant.OnSslChanged(mServerTextField.value, evt.newValue);
mServerTextField.value = mUserAssistant.GetProposedServer();
}
void InitializeLayoutAndStyles()
{
VisualElement root = rootVisualElement;
root.LoadLayout(typeof(TeamEditionConfigurationWindow).Name);
root.LoadStyle(typeof(TeamEditionConfigurationWindow).Name);
}
void OnServerTextFieldChanged(ChangeEvent<string> evt)
{
mUserAssistant.OnServerChanged(evt.newValue);
mUseSslToggle.value = mUserAssistant.IsSslServer(evt.newValue);
}
void ShowServerNotificationMessage(string message)
{
mConnectedLabel.text = message;
mConnectedLabel.Show();
}
void ShowServerValidationError(string message)
{
mConnectedLabel.text = message;
mConnectedLabel.AddToClassList("error");
mConnectedLabel.Show();
}
void ShowCredentialsNotificationMessage(string message)
{
mCredentialsLabel.text = message;
mCredentialsLabel.Show();
}
void ShowCheckCredentialsError(string message)
{
mCredentialsLabel.text = message;
mCredentialsLabel.AddToClassList("error");
mCredentialsLabel.Show();
}
void HideValidation()
{
mConnectedLabel.RemoveFromClassList("error");
mConnectedLabel.Hide();
}
void ShowProgress(string text)
{
mSpinnerLabel.text = text;
mSpinnerContainer.Show();
mSpinnerLabel.Show();
mLoadingSpinner.Start();
}
void HideProgress()
{
mLoadingSpinner.Stop();
mSpinnerContainer.Hide();
mSpinnerLabel.Hide();
}
void UpdatePasswordEntries(bool bIsPasswordRequired)
{
if (!bIsPasswordRequired)
{
mPasswordTextField.Collapse();
mUserTextField.SetEnabled(false);
mUserTextField.value = Environment.UserName;
return;
}
mUserTextField.SetEnabled(true);
mPasswordTextField.Show();
mUserTextField.SelectAll();
mUserTextField.FocusWorkaround();
}
Button mConnectButton;
Label mConnectedLabel;
TextField mServerTextField;
TextField mPasswordTextField;
Toggle mUseSslToggle;
LoadingSpinner mLoadingSpinner;
Label mSpinnerLabel;
VisualElement mSpinnerContainer;
Button mCheckConnectionButton;
Label mCredentialsLabel;
Button mOkButton;
Button mCancelButton;
SEIDWorkingMode mSEIDWorkingMode = SEIDWorkingMode.UPWorkingMode;
ConfigurationDialogUserAssistant mUserAssistant =
new ConfigurationDialogUserAssistant();
IPlasticWebRestApi mRestApi;
WelcomeView mWelcomeView;
TextField mUserTextField;
}
}

Some files were not shown because too many files have changed in this diff Show More