@@ -30,6 +30,7 @@ namespace Teknik.Configuration | |||
private BlogConfig _BlogConfig; | |||
private ApiConfig _ApiConfig; | |||
private PodcastConfig _PodcastConfig; | |||
private DatabaseConfig _DatabaseConfig; | |||
public bool DevEnvironment { get { return _DevEnvironment; } set { _DevEnvironment = value; } } | |||
@@ -70,6 +71,9 @@ namespace Teknik.Configuration | |||
// Podcast Configuration | |||
public PodcastConfig PodcastConfig { get { return _PodcastConfig; } set { _PodcastConfig = value; } } | |||
// Database Configuration | |||
public DatabaseConfig DatabaseConfig { get { return _DatabaseConfig; } set { _DatabaseConfig = value; } } | |||
public Config() | |||
{ | |||
_ConfigRWLock = new ReaderWriterLockSlim(); | |||
@@ -100,6 +104,7 @@ namespace Teknik.Configuration | |||
PasteConfig = new PasteConfig(); | |||
ApiConfig = new ApiConfig(); | |||
PodcastConfig = new PodcastConfig(); | |||
DatabaseConfig = new DatabaseConfig(); | |||
} | |||
public static Config Deserialize(string text) |
@@ -0,0 +1,27 @@ | |||
namespace Teknik.Configuration | |||
{ | |||
public class DatabaseConfig | |||
{ | |||
public bool Migrate { get; set; } | |||
public string Server { get; set; } | |||
public int Port { get; set; } | |||
public string Database { get; set; } | |||
public string Username { get; set; } | |||
public string Password { get; set; } | |||
public DatabaseConfig() | |||
{ | |||
SetDefaults(); | |||
} | |||
public void SetDefaults() | |||
{ | |||
Migrate = false; | |||
Server = "localhost"; | |||
Port = 3306; | |||
Database = string.Empty; | |||
Username = string.Empty; | |||
Password = string.Empty; | |||
} | |||
} | |||
} |
@@ -9,7 +9,7 @@ namespace Teknik.Helpers | |||
public static class Constants | |||
{ | |||
// Blog Constants | |||
public static int SERVERBLOGID = 1; | |||
public static int SERVERBLOGID = 0; | |||
// Paste Constants | |||
public static Dictionary<string, string> HIGHLIGHTFORMATS = new Dictionary<string, string>() |
@@ -0,0 +1,174 @@ | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Threading; | |||
using Teknik.Configuration; | |||
using MySql.Data.MySqlClient; | |||
namespace Teknik.Helpers | |||
{ | |||
public class MysqlDatabase | |||
{ | |||
public event EventHandler<string> MysqlErrorEvent; | |||
private bool Connected { get; set; } | |||
private MySqlConnection Connection { get; set; } | |||
private ReaderWriterLockSlim DatabaseLock { get; set; } | |||
public MysqlDatabase(DatabaseConfig config) | |||
{ | |||
Connected = false; | |||
Connection = null; | |||
DatabaseLock = new ReaderWriterLockSlim(); | |||
Connect(config); | |||
} | |||
public List<Dictionary<string, object>> Query(string query, params object[] args) | |||
{ | |||
List<Dictionary<string, object>> rows = new List<Dictionary<string, object>>(); | |||
if (Connected) | |||
{ | |||
DatabaseLock.EnterWriteLock(); | |||
MySqlCommand cmd = PrepareQuery(query, args); | |||
try | |||
{ | |||
MySqlDataReader reader = cmd.ExecuteReader(); | |||
while (reader.Read()) | |||
{ | |||
Dictionary<string, object> row = new Dictionary<string, object>(); | |||
for (int i = 0; i < reader.FieldCount; i++) | |||
{ | |||
row.Add(reader.GetName(i), reader.GetValue(i)); | |||
} | |||
rows.Add(row); | |||
} | |||
reader.Close(); | |||
} | |||
catch (MySqlException exception) | |||
{ | |||
if (MysqlErrorEvent != null) | |||
{ | |||
MysqlErrorEvent(this, exception.Message); | |||
} | |||
} | |||
catch (Exception exception) | |||
{ | |||
if (MysqlErrorEvent != null) | |||
{ | |||
MysqlErrorEvent(this, exception.Message); | |||
} | |||
} | |||
DatabaseLock.ExitWriteLock(); | |||
} | |||
return rows; | |||
} | |||
public object ScalarQuery(string query, params object[] args) | |||
{ | |||
if (Connected) | |||
{ | |||
DatabaseLock.EnterWriteLock(); | |||
MySqlCommand cmd = PrepareQuery(query, args); | |||
object result = null; | |||
try | |||
{ | |||
result = cmd.ExecuteScalar(); | |||
} | |||
catch (MySqlException exception) | |||
{ | |||
if (MysqlErrorEvent != null) | |||
{ | |||
MysqlErrorEvent(this, exception.Message); | |||
} | |||
} | |||
catch (Exception exception) | |||
{ | |||
if (MysqlErrorEvent != null) | |||
{ | |||
MysqlErrorEvent(this, exception.Message); | |||
} | |||
} | |||
DatabaseLock.ExitWriteLock(); | |||
return result; | |||
} | |||
return null; | |||
} | |||
public void Execute(string query, params object[] args) | |||
{ | |||
if (Connected) | |||
{ | |||
DatabaseLock.EnterWriteLock(); | |||
MySqlCommand cmd = PrepareQuery(query, args); | |||
try | |||
{ | |||
int result = cmd.ExecuteNonQuery(); | |||
} | |||
catch (MySqlException exception) | |||
{ | |||
if (MysqlErrorEvent != null) | |||
{ | |||
MysqlErrorEvent(this, exception.Message); | |||
} | |||
} | |||
catch (Exception exception) | |||
{ | |||
if (MysqlErrorEvent != null) | |||
{ | |||
MysqlErrorEvent(this, exception.Message); | |||
} | |||
} | |||
DatabaseLock.ExitWriteLock(); | |||
} | |||
} | |||
private void Connect(DatabaseConfig config) | |||
{ | |||
if (Connection == null) | |||
{ | |||
if (config.Server != string.Empty && config.Database != string.Empty && config.Username != string.Empty && config.Password != string.Empty) | |||
{ | |||
string strCon = string.Format("Server={0}; database={1}; user={2}; password={3}; port={4}; charset=utf8", config.Server, config.Database, config.Username, config.Password, config.Port); | |||
Connection = new MySqlConnection(strCon); | |||
try | |||
{ | |||
Connection.Open(); | |||
Connected = true; | |||
} | |||
catch (MySqlException ex) | |||
{ | |||
Connected = false; | |||
} | |||
} | |||
} | |||
} | |||
private void Disconnect() | |||
{ | |||
if (Connection != null && Connected) | |||
{ | |||
Connected = false; | |||
Connection.Close(); | |||
} | |||
} | |||
private MySqlCommand PrepareQuery(string query, object[] args) | |||
{ | |||
if (Connected) | |||
{ | |||
MySqlCommand cmd = new MySqlCommand(); | |||
cmd.Connection = Connection; | |||
for (int i = 0; i < args.Length; i++) | |||
{ | |||
string param = "{" + i + "}"; | |||
string paramName = "@DBVar_" + i; | |||
query = query.Replace(param, paramName); | |||
cmd.Parameters.AddWithValue(paramName, args[i]); | |||
} | |||
cmd.CommandText = query; | |||
return cmd; | |||
} | |||
return null; | |||
} | |||
} | |||
} |
@@ -1,11 +1,15 @@ | |||
namespace Teknik.Migrations | |||
{ | |||
using Areas.Paste; | |||
using Helpers; | |||
using System; | |||
using System.Collections.Generic; | |||
using System.Data.Entity; | |||
using System.Data.Entity.Migrations; | |||
using System.Linq; | |||
using Teknik.Configuration; | |||
internal sealed class Configuration : DbMigrationsConfiguration<Teknik.Models.TeknikEntities> | |||
internal sealed class Configuration : DbMigrationsConfiguration<Models.TeknikEntities> | |||
{ | |||
public Configuration() | |||
{ | |||
@@ -13,20 +17,188 @@ namespace Teknik.Migrations | |||
AutomaticMigrationDataLossAllowed = true; | |||
} | |||
protected override void Seed(Teknik.Models.TeknikEntities context) | |||
protected override void Seed(Models.TeknikEntities context) | |||
{ | |||
// This method will be called after migrating to the latest version. | |||
// You can use the DbSet<T>.AddOrUpdate() helper extension method | |||
// to avoid creating duplicate seed data. E.g. | |||
// | |||
// context.People.AddOrUpdate( | |||
// p => p.FullName, | |||
// new Person { FullName = "Andrew Peters" }, | |||
// new Person { FullName = "Brice Lambson" }, | |||
// new Person { FullName = "Rowan Miller" } | |||
// ); | |||
// | |||
Config config = Config.Load(); | |||
// Pre-populate with the default stuff | |||
// Create server blog | |||
Areas.Blog.Models.Blog serverBlog = new Areas.Blog.Models.Blog(); | |||
context.Blogs.Add(serverBlog); | |||
context.SaveChanges(); | |||
// Create roles and groups | |||
Areas.Profile.Models.Role adminRole = new Areas.Profile.Models.Role(); | |||
adminRole.Name = "Admin"; | |||
adminRole.Description = "Allows complete access to user specific actions"; | |||
context.Roles.Add(adminRole); | |||
Areas.Profile.Models.Role podcastRole = new Areas.Profile.Models.Role(); | |||
podcastRole.Name = "Podcast"; | |||
podcastRole.Description = "Allows create/edit/delete access to podcasts"; | |||
context.Roles.Add(podcastRole); | |||
Areas.Profile.Models.Group adminGroup = new Areas.Profile.Models.Group(); | |||
adminGroup.Name = "Administrators"; | |||
adminGroup.Description = "System Administrators with full access"; | |||
adminGroup.Roles.Add(adminRole); | |||
adminGroup.Roles.Add(podcastRole); | |||
Areas.Profile.Models.Group podcastGroup = new Areas.Profile.Models.Group(); | |||
podcastGroup.Name = "Podcast"; | |||
podcastGroup.Description = "Podcast team members"; | |||
podcastGroup.Roles.Add(podcastRole); | |||
context.SaveChanges(); | |||
if (config.DatabaseConfig.Migrate && !config.DevEnvironment) | |||
{ | |||
// Convert legacy MySQL DB to new MS SQL DB | |||
MysqlDatabase db = new MysqlDatabase(config.DatabaseConfig); | |||
// Transfer transactions | |||
var transRet = db.Query("SELECT * FROM transactions"); | |||
foreach (var tran in transRet) | |||
{ | |||
switch (tran["trans_type"].ToString()) | |||
{ | |||
case "One-Time": | |||
Areas.Transparency.Models.OneTime tr = new Areas.Transparency.Models.OneTime(); | |||
tr.DateSent = DateTime.Parse(tran["date_posted"].ToString()); | |||
tr.Amount = Int32.Parse(tran["amount"].ToString()); | |||
tr.Currency = tran["currency"].ToString(); | |||
tr.Recipient = tran["recipient"].ToString(); | |||
tr.Reason = tran["reason"].ToString(); | |||
context.Transactions.Add(tr); | |||
break; | |||
case "Bill": | |||
Areas.Transparency.Models.Bill bill = new Areas.Transparency.Models.Bill(); | |||
bill.DateSent = DateTime.Parse(tran["date_posted"].ToString()); | |||
bill.Amount = Int32.Parse(tran["amount"].ToString()); | |||
bill.Currency = tran["currency"].ToString(); | |||
bill.Recipient = tran["recipient"].ToString(); | |||
bill.Reason = tran["reason"].ToString(); | |||
context.Transactions.Add(bill); | |||
break; | |||
case "Donation": | |||
Areas.Transparency.Models.Donation don = new Areas.Transparency.Models.Donation(); | |||
don.DateSent = DateTime.Parse(tran["date_posted"].ToString()); | |||
don.Amount = Int32.Parse(tran["amount"].ToString()); | |||
don.Currency = tran["currency"].ToString(); | |||
don.Sender = tran["sender"].ToString(); | |||
don.Reason = tran["reason"].ToString(); | |||
context.Transactions.Add(don); | |||
break; | |||
} | |||
} | |||
context.SaveChanges(); | |||
// Transfer Users and Blogs/Posts | |||
Dictionary<int, int> userMapping = new Dictionary<int, int>(); | |||
Dictionary<int, int> postMapping = new Dictionary<int, int>(); | |||
var userRet = db.Query("SELECT * FROM users"); | |||
foreach (var user in userRet) | |||
{ | |||
// Create User | |||
Areas.Profile.Models.User newUser = new Areas.Profile.Models.User(); | |||
newUser.UserSettings = new Areas.Profile.Models.UserSettings(); | |||
newUser.BlogSettings = new Areas.Profile.Models.BlogSettings(); | |||
newUser.UploadSettings = new Areas.Profile.Models.UploadSettings(); | |||
newUser.TransferAccount = true; | |||
newUser.Username = user["username"].ToString(); | |||
newUser.HashedPassword = user["password"].ToString(); | |||
newUser.JoinDate = DateTime.Parse(user["join_date"].ToString()); | |||
newUser.LastSeen = DateTime.Parse(user["last_seen"].ToString()); | |||
newUser.UserSettings.About = user["about"].ToString(); | |||
newUser.UserSettings.Website = user["website"].ToString(); | |||
newUser.UserSettings.Quote = user["quote"].ToString(); | |||
newUser.BlogSettings.Title = user["blog_title"].ToString(); | |||
newUser.BlogSettings.Description = user["blog_desc"].ToString(); | |||
context.Users.Add(newUser); | |||
context.SaveChanges(); | |||
int userId = newUser.UserId; | |||
userMapping.Add(Int32.Parse(user["id"].ToString()), userId); | |||
// Create Blog for user | |||
Areas.Blog.Models.Blog newBlog = new Areas.Blog.Models.Blog(); | |||
newBlog.UserId = userId; | |||
context.SaveChanges(); | |||
int blogId = newBlog.BlogId; | |||
// Transfer Blog Posts | |||
var postRet = db.Query("SELECT * FROM blog WHERE author_id={0}", new object[] { userId }); | |||
if (postRet != null) | |||
{ | |||
foreach (var post in postRet) | |||
{ | |||
// Create new Blog Post | |||
Areas.Blog.Models.BlogPost newPost = new Areas.Blog.Models.BlogPost(); | |||
if (post["user_id"].ToString() == "0") | |||
{ | |||
newPost.BlogId = 0; | |||
newPost.System = true; | |||
} | |||
else | |||
{ | |||
newPost.BlogId = blogId; | |||
} | |||
newPost.DatePosted = DateTime.Parse(post["date_posted"].ToString()); | |||
newPost.DatePublished = DateTime.Parse(post["date_published"].ToString()); | |||
newPost.DateEdited = DateTime.Parse(post["date_published"].ToString()); | |||
newPost.Published = (post["published"].ToString() == "1"); | |||
newPost.Title = post["title"].ToString(); | |||
newPost.Article = post["post"].ToString(); | |||
context.BlogPosts.Add(newPost); | |||
context.SaveChanges(); | |||
postMapping.Add(Int32.Parse(post["id"].ToString()), newPost.BlogPostId); | |||
} | |||
} | |||
} | |||
// Transfer Blog Comments | |||
var commentRet = db.Query("SELECT * FROM comments WHERE service = 'blog'"); | |||
foreach (var comment in commentRet) | |||
{ | |||
int postId = Int32.Parse(comment["reply_id"].ToString()); | |||
int userId = Int32.Parse(comment["user_id"].ToString()); | |||
if (postMapping.ContainsKey(postId) && userMapping.ContainsKey(userId)) | |||
{ | |||
Areas.Blog.Models.BlogPostComment newComment = new Areas.Blog.Models.BlogPostComment(); | |||
newComment.BlogPostId = postMapping[postId]; | |||
newComment.UserId = userMapping[userId]; | |||
newComment.Article = comment["post"].ToString(); | |||
newComment.DatePosted = DateTime.Parse(comment["date_posted"].ToString()); | |||
newComment.DateEdited = DateTime.Parse(comment["date_posted"].ToString()); | |||
context.BlogComments.Add(newComment); | |||
context.SaveChanges(); | |||
} | |||
} | |||
// Transfer Pastes | |||
var pasteRet = db.Query("SELECT * FROM paste"); | |||
foreach (var paste in pasteRet) | |||
{ | |||
// If it's a password protected paste, we just skip it | |||
if (paste["password"] == null) | |||
{ | |||
string content = paste["code"].ToString(); | |||
string title = paste["title"].ToString(); | |||
DateTime posted = DateTime.Parse(paste["posted"].ToString()); | |||
int userId = Int32.Parse(paste["user_id"].ToString()); | |||
Areas.Paste.Models.Paste newPaste = PasteHelper.CreatePaste(content, title); | |||
newPaste.DatePosted = posted; | |||
newPaste.Url = paste["pid"].ToString(); | |||
if (userMapping.ContainsKey(userId) && userId != 0) | |||
{ | |||
newPaste.UserId = userMapping[userId]; | |||
} | |||
context.Pastes.Add(newPaste); | |||
context.SaveChanges(); | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} |
@@ -72,6 +72,10 @@ | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Microsoft.CSharp" /> | |||
<Reference Include="MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\MySql.Data.6.9.8\lib\net45\MySql.Data.dll</HintPath> | |||
<Private>True</Private> | |||
</Reference> | |||
<Reference Include="Newtonsoft.Json, Version=8.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL"> | |||
<HintPath>..\packages\Newtonsoft.Json.8.0.2\lib\net45\Newtonsoft.Json.dll</HintPath> | |||
<Private>True</Private> | |||
@@ -219,6 +223,7 @@ | |||
<Compile Include="Areas\Upload\ViewModels\DownloadViewModel.cs" /> | |||
<Compile Include="Areas\Upload\ViewModels\UploadViewModel.cs" /> | |||
<Compile Include="Configuration\ApiConfig.cs" /> | |||
<Compile Include="Configuration\DatabaseConfig.cs" /> | |||
<Compile Include="Configuration\EmailConfig.cs" /> | |||
<Compile Include="Configuration\GitConfig.cs" /> | |||
<Compile Include="Configuration\PodcastConfig.cs" /> | |||
@@ -241,6 +246,7 @@ | |||
<Compile Include="Areas\Profile\Models\PermissionType.cs" /> | |||
<Compile Include="Areas\Blog\Models\BlogPost.cs" /> | |||
<Compile Include="Areas\Profile\Models\Role.cs" /> | |||
<Compile Include="Helpers\MysqlDatabase.cs" /> | |||
<Compile Include="Helpers\MarkdownHelper.cs" /> | |||
<Compile Include="Helpers\RSSFeedResult.cs" /> | |||
<Compile Include="Helpers\UrlExtensions.cs" /> |
@@ -85,4 +85,9 @@ | |||
<provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" /> | |||
</providers> | |||
</entityFramework> | |||
</configuration> | |||
<system.data> | |||
<DbProviderFactories> | |||
<remove invariant="MySql.Data.MySqlClient" /> | |||
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" /> | |||
</DbProviderFactories> | |||
</system.data></configuration> |
@@ -22,6 +22,7 @@ | |||
<package id="Microsoft.jQuery.Unobtrusive.Validation" version="3.2.3" targetFramework="net46" userInstalled="true" /> | |||
<package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net46" userInstalled="true" /> | |||
<package id="Modernizr" version="2.8.3" targetFramework="net452" userInstalled="true" /> | |||
<package id="MySql.Data" version="6.9.8" targetFramework="net452" /> | |||
<package id="Newtonsoft.Json" version="8.0.2" targetFramework="net452" userInstalled="true" /> | |||
<package id="Respond" version="1.4.2" targetFramework="net452" userInstalled="true" /> | |||
<package id="routedebugger" version="2.1.4" targetFramework="net452" userInstalled="true" /> |