diff --git a/WebDeployHelper/WebDeployHelper.sln b/WebDeployHelper/WebDeployHelper.sln
new file mode 100644
index 0000000..5446a9c
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25123.0
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebDeployHelper", "WebDeployHelper\WebDeployHelper.csproj", "{C43BDD5D-FDC9-45BF-B2C8-233F9E339624}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {C43BDD5D-FDC9-45BF-B2C8-233F9E339624}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {C43BDD5D-FDC9-45BF-B2C8-233F9E339624}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {C43BDD5D-FDC9-45BF-B2C8-233F9E339624}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {C43BDD5D-FDC9-45BF-B2C8-233F9E339624}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/WebDeployHelper/WebDeployHelper/ConfigReader.cs b/WebDeployHelper/WebDeployHelper/ConfigReader.cs
new file mode 100644
index 0000000..e65e5a4
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/ConfigReader.cs
@@ -0,0 +1,109 @@
+using System;
+using System.IO;
+
+namespace WebDeployHelper
+{
+ class ConfigReader
+ {
+ #region Read Config
+
+ private readonly string _configDirectory;
+
+ private static string _configDirUpload;
+ private static string _configReleaseType;
+ private static string _configSftpAddress;
+ private static string _configSftpUser;
+ private static string _configDevPath;
+ private static string _configReleasePath;
+
+ private static string _remotePath;
+
+ public ConfigReader()
+ {
+ _configDirectory = DeployConfig.DirConfig;
+ }
+
+ public ConfigReader(string configDirectory, string vstoDirectory)
+ {
+ _configDirectory = configDirectory;
+ }
+
+ public ConfigReader ReadConfig()
+ {
+ //Sequence matters
+ InitConfigVariables();
+ InitRemotePath();
+ InitDeployInfo();
+ return this;
+ }
+
+ public DeployConfig ToDeployConfig()
+ {
+ var config = new DeployConfig
+ {
+ ConfigDirUpload = _configDirUpload,
+ ConfigReleaseType = _configReleaseType,
+ ConfigSftpAddress = _configSftpAddress,
+ ConfigSftpUser = _configSftpUser,
+ RemotePath = _remotePath
+ };
+ return config;
+ }
+
+ private void InitConfigVariables()
+ {
+ string[] configContent = {};
+ try
+ {
+ configContent = File.ReadAllLines(_configDirectory);
+ }
+ catch (Exception e)
+ {
+ Util.DisplayWarning(TextCollection.ErrorNoConfig, e);
+ }
+
+ //index here refers to the line number in DeployHelper.conf
+ _configDirUpload = configContent[1];
+ _configReleaseType = configContent[3];
+ _configSftpAddress = configContent[5];
+ _configSftpUser = configContent[7];
+ _configDevPath = configContent[9];
+ _configReleasePath = configContent[11];
+ }
+
+ private void InitRemotePath()
+ {
+ switch (_configReleaseType)
+ {
+ case TextCollection.VarRelease:
+ _remotePath = _configReleasePath;
+ break;
+ case TextCollection.VarDev:
+ _remotePath = _configDevPath;
+ break;
+ default:
+ Util.DisplayWarning(TextCollection.ErrorInvalidReleaseType, new Exception());
+ break;
+ }
+ }
+
+ private void InitDeployInfo()
+ {
+ PrintInfo("You are going to deploy PowerPointLabs Website");
+ PrintInfo("");
+ PrintInfo("Settings info:");
+ PrintInfo("Upload Directory: ", _configDirUpload);
+ PrintInfo("Release Type: ", _configReleaseType);
+ PrintInfo("Remote Path: ", _remotePath);
+ PrintInfo("");
+ }
+
+ private void PrintInfo(string text, string highlightedText = "")
+ {
+ Console.Write(text);
+ Util.ConsoleWriteWithColor(highlightedText, ConsoleColor.Yellow);
+ Console.WriteLine("");
+ }
+ #endregion
+ }
+}
diff --git a/WebDeployHelper/WebDeployHelper/DeployConfig.cs b/WebDeployHelper/WebDeployHelper/DeployConfig.cs
new file mode 100644
index 0000000..31a1638
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/DeployConfig.cs
@@ -0,0 +1,16 @@
+using System;
+
+namespace WebDeployHelper
+{
+ class DeployConfig
+ {
+ public static readonly string DirConfig = Environment.CurrentDirectory + @"\WebDeployHelper.conf";
+
+ public string ConfigDirUpload;
+ public string ConfigReleaseType;
+ public string ConfigSftpAddress;
+ public string ConfigSftpUser;
+
+ public string RemotePath;
+ }
+}
diff --git a/WebDeployHelper/WebDeployHelper/DeployUploader.cs b/WebDeployHelper/WebDeployHelper/DeployUploader.cs
new file mode 100644
index 0000000..e6ee72b
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/DeployUploader.cs
@@ -0,0 +1,34 @@
+using System;
+
+namespace WebDeployHelper
+{
+ class DeployUploader
+ {
+ #region SFTP upload
+
+ private readonly DeployConfig _config;
+
+ public DeployUploader(DeployConfig config)
+ {
+ _config = config;
+ }
+
+ public void SftpUpload()
+ {
+ try
+ {
+ Console.WriteLine("Connecting the server...");
+ var uploader = new Uploader();
+ uploader.SetConfig(_config);
+ uploader.Upload();
+ }
+ catch (Exception e)
+ {
+ Console.WriteLine("Error during SFTP uploading:");
+ Util.DisplayWarning(e.Message, e);
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/WebDeployHelper/WebDeployHelper/Program.cs b/WebDeployHelper/WebDeployHelper/Program.cs
new file mode 100644
index 0000000..3cb47ce
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/Program.cs
@@ -0,0 +1,65 @@
+using System;
+#region WebDeployHelper Description
+//
+// WebDeployHelper Class
+// ------------------
+// Simply double click the .exe file to patch PowerPointLabs Website,
+// so that it uploads the files onto the PowerPointLabs server.
+//
+// HOW TO USE
+//
+// For the first time use, you need to setup the followings:
+//
+// 0. Compile WebDeployHelper using Visual Studio. .NET 4.5 is required. The output program is under bin/debug or bin/release folder.
+//
+// 1. Fill in WebDeployHelper.conf
+// - Upload directory is the local directory to upload to the server
+// - Release type is dev or release
+// - SFTP address is the server to upload to
+// - SFTP username is the username used to login the server
+// - Dev path is the installation folder path on the server for dev version PowerPointLabs Website
+// - Release path is the installation folder path on the server for release version PowerPointLabs Website
+//
+// 2. Copy WebDeployHelper.exe, WebDeployHelper.conf, WinSCP.exe and WinSCP.com from the output folder to the publish folder
+//
+// 3. Run WebDeployHelper.exe and follow the instructions.
+//
+// For the next time
+//
+// 0. Run WebDeployHelper.exe and follow the instructions.
+//
+// Have a nice day :)
+//
+//TODO: add testing
+#endregion
+namespace WebDeployHelper
+{
+ class Program
+ {
+ public static void Main(string[] args)
+ {
+ try
+ {
+ # region Init
+
+ var config = new ConfigReader()
+ .ReadConfig()
+ .ToDeployConfig();
+
+ # endregion
+
+ new DeployUploader(config)
+ .SftpUpload();
+ Util.DisplayEndMessage();
+ }
+ catch
+ {
+ Util.IgnoreException();
+ }
+ finally
+ {
+ Console.ReadKey();
+ }
+ }
+ }
+}
diff --git a/WebDeployHelper/WebDeployHelper/Properties/AssemblyInfo.cs b/WebDeployHelper/WebDeployHelper/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ccbebf6
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("DeployHelper")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("DeployHelper")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("57277935-16d4-471b-9706-49fca9fa69cb")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/WebDeployHelper/WebDeployHelper/TextCollection.cs b/WebDeployHelper/WebDeployHelper/TextCollection.cs
new file mode 100644
index 0000000..3b798c5
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/TextCollection.cs
@@ -0,0 +1,21 @@
+
+namespace WebDeployHelper
+{
+ class TextCollection
+ {
+ public const string ErrorNoConfig = "Cannot find config file.";
+ public const string ErrorInvalidReleaseType = "Invalid release type in config file.";
+
+ public const string DoneUploaded = "Uploaded.";
+
+ public const string InfoEnterPassword = "Enter SFTP password: ";
+
+ public const string VarDev = "dev";
+ public const string VarRelease = "release";
+
+ public const string FileXmlLogName = "log.xml";
+
+ public const string PermissionsFile = "644";
+ public const string PermissionsDirectory = "755";
+ }
+}
diff --git a/WebDeployHelper/WebDeployHelper/Uploader.cs b/WebDeployHelper/WebDeployHelper/Uploader.cs
new file mode 100644
index 0000000..1f93d57
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/Uploader.cs
@@ -0,0 +1,94 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Text;
+using System.Xml;
+using System.Xml.XPath;
+
+namespace WebDeployHelper
+{
+ class Uploader
+ {
+ private const bool IsToRemoveAfterUpload = false;
+
+ protected DeployConfig Config;
+
+ public void SetConfig(DeployConfig config)
+ {
+ Config = config;
+ }
+
+ public void Upload()
+ {
+ var winscp = new Process();
+ winscp.StartInfo.FileName = "winscp.com";
+ winscp.StartInfo.Arguments = "/xmllog=\"" + TextCollection.FileXmlLogName + "\"";
+ winscp.StartInfo.UseShellExecute = false;
+ winscp.StartInfo.CreateNoWindow = true;
+ winscp.StartInfo.RedirectStandardInput = true;
+ winscp.Start();
+
+ var writer = winscp.StandardInput;
+
+ WriteUploadCommands(writer);
+
+ writer.Close();
+ winscp.WaitForExit();
+
+ CheckForError();
+ }
+
+ private void WriteUploadCommands(StreamWriter writer)
+ {
+ writer.WriteLine("open sftp://" + Config.ConfigSftpUser + "@" + Config.ConfigSftpAddress + ":22 -hostkey=\"*\"");
+
+ string password = null;
+ while (password == null || password.Trim() == "")
+ {
+ Console.Write(TextCollection.InfoEnterPassword);
+ password = Util.ReadPassword();
+ }
+ writer.WriteLine(password);
+ password = null;
+
+ writer.WriteLine("option confirm off");
+ writer.WriteLine("put -permissions=" + TextCollection.PermissionsFile + " -transfer=binary \"" + Config.ConfigDirUpload
+ + "\" \"" + Config.RemotePath + "\"");
+
+ var directoryInfos = new DirectoryInfo(Config.ConfigDirUpload).EnumerateDirectories("*", SearchOption.AllDirectories);
+
+ foreach (var directoryInfo in directoryInfos)
+ {
+ string remoteDirectoryPath = Config.RemotePath + directoryInfo.FullName.Replace(Config.ConfigDirUpload, "").Replace("\\", "/");
+
+ writer.WriteLine("chmod " + TextCollection.PermissionsDirectory + " \"" + remoteDirectoryPath + "\"");
+ }
+
+ writer.WriteLine("exit");
+ }
+
+ private void CheckForError()
+ {
+ var log = new XPathDocument(TextCollection.FileXmlLogName);
+ var xmlNamespace = new XmlNamespaceManager(new NameTable());
+ xmlNamespace.AddNamespace("w", "http://winscp.net/schema/session/1.0");
+ var navigator = log.CreateNavigator();
+ var iterator = navigator.Select("//w:message", xmlNamespace);
+
+ if (iterator.Count > 0)
+ {
+ var errorMessage = new StringBuilder();
+ foreach (XPathNavigator message in iterator)
+ {
+ errorMessage.AppendLine(message.Value);
+ }
+
+ throw new Exception(errorMessage.ToString());
+ }
+ else
+ {
+ Console.WriteLine(TextCollection.DoneUploaded);
+ }
+ }
+ }
+}
diff --git a/WebDeployHelper/WebDeployHelper/Util.cs b/WebDeployHelper/WebDeployHelper/Util.cs
new file mode 100644
index 0000000..8c07fba
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/Util.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Text;
+
+namespace WebDeployHelper
+{
+ class Util
+ {
+ #region Helper functions
+
+ public static void ConsoleWriteWithColor(String content, ConsoleColor color)
+ {
+ Console.ForegroundColor = color;
+ Console.Write(content);
+ Console.ResetColor();
+ }
+
+ public static void IgnoreException()
+ {
+ }
+
+ public static void DisplayWarning(string content, Exception e)
+ {
+ ConsoleWriteWithColor(content, ConsoleColor.Red);
+ throw new InvalidOperationException(content, e);
+ }
+
+ public static void DisplayDone(string content)
+ {
+ ConsoleWriteWithColor(content + "\n", ConsoleColor.Green);
+ }
+
+ public static void DisplayEndMessage()
+ {
+ DisplayDone("All Done!");
+ Console.WriteLine("Have a nice day :)");
+ }
+
+ public static string ReadPassword()
+ {
+ StringBuilder password = new StringBuilder();
+ ConsoleKeyInfo info = Console.ReadKey(true);
+ while (info.Key != ConsoleKey.Enter)
+ {
+ if (info.Key != ConsoleKey.Backspace)
+ {
+ password.Append(info.KeyChar);
+ }
+ else if (info.Key == ConsoleKey.Backspace)
+ {
+ if (password.Length > 0)
+ {
+ password.Length--;
+ }
+ }
+ info = Console.ReadKey(true);
+ }
+ // add a new line because user pressed enter at the end of their password
+ Console.WriteLine();
+ return password.ToString();
+ }
+
+ #endregion
+ }
+}
diff --git a/WebDeployHelper/WebDeployHelper/WebDeployHelper.conf b/WebDeployHelper/WebDeployHelper/WebDeployHelper.conf
new file mode 100644
index 0000000..a6a5704
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/WebDeployHelper.conf
@@ -0,0 +1,12 @@
+//upload directory
+...
+//release type
+dev
+//SFTP address
+...
+//SFTP username
+...
+//Dev path
+...
+//Release path
+...
\ No newline at end of file
diff --git a/WebDeployHelper/WebDeployHelper/WebDeployHelper.csproj b/WebDeployHelper/WebDeployHelper/WebDeployHelper.csproj
new file mode 100644
index 0000000..3b6642a
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/WebDeployHelper.csproj
@@ -0,0 +1,96 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {C43BDD5D-FDC9-45BF-B2C8-233F9E339624}
+ Exe
+ Properties
+ WebDeployHelper
+ WebDeployHelper
+ v4.5
+ 512
+
+ publish\
+ true
+ Disk
+ false
+ Foreground
+ 7
+ Days
+ false
+ false
+ true
+ 0
+ 1.0.0.%2a
+ false
+ false
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+ false
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ False
+ Microsoft .NET Framework 4.5 %28x86 and x64%29
+ true
+
+
+ False
+ .NET Framework 3.5 SP1
+ false
+
+
+
+
+
\ No newline at end of file
diff --git a/WebDeployHelper/WebDeployHelper/app.config b/WebDeployHelper/WebDeployHelper/app.config
new file mode 100644
index 0000000..c5e1dae
--- /dev/null
+++ b/WebDeployHelper/WebDeployHelper/app.config
@@ -0,0 +1,3 @@
+
+
+