diff --git a/AssemblyVersion.cs b/AssemblyVersion.cs index 00d550d..d4f86b1 100644 --- a/AssemblyVersion.cs +++ b/AssemblyVersion.cs @@ -16,5 +16,5 @@ using System.Reflection; // // You can specify all the values or you can default the Revision and Build Numbers // by using the '*' as shown below: -[assembly: AssemblyVersion("3.0.5.613")] -[assembly: AssemblyFileVersion("3.0.5.613")] \ No newline at end of file +[assembly: AssemblyVersion("3.0.5.614")] +[assembly: AssemblyFileVersion("3.0.5.614")] \ No newline at end of file diff --git a/BackupRestore/BackupRestore.cs b/BackupRestore/BackupRestore.cs new file mode 100644 index 0000000..dd77e9d --- /dev/null +++ b/BackupRestore/BackupRestore.cs @@ -0,0 +1,673 @@ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ScrewTurn.Wiki.PluginFramework; +using System.Web.Script.Serialization; +using System.Collections; +using ScrewTurn.Wiki.AclEngine; +using Ionic.Zip; +using System.IO; + +namespace ScrewTurn.Wiki.BackupRestore { + + /// + /// Implements a Backup and Restore procedure for settings storage providers. + /// + public static class BackupRestore { + + private const string BACKUP_RESTORE_UTILITY_VERSION = "1.0"; + + private static VersionFile generateVersionFile(string backupName) { + return new VersionFile() { + BackupRestoreVersion = BACKUP_RESTORE_UTILITY_VERSION, + WikiVersion = typeof(BackupRestore).Assembly.GetName().Version.ToString(), + BackupName = backupName + }; + } + + /// + /// Backups all the providers (excluded global settings storage provider). + /// + /// The name of the zip file where to store the backup file. + /// The available plugins. + /// The settings storage provider. + /// The pages storage providers. + /// The users storage providers. + /// The files storage providers. + /// true if the backup has been succesfull. + public static bool BackupAll(string backupZipFileName, string[] plugins, ISettingsStorageProviderV30 settingsStorageProvider, IPagesStorageProviderV30[] pagesStorageProviders, IUsersStorageProviderV30[] usersStorageProviders, IFilesStorageProviderV30[] filesStorageProviders) { + string tempPath = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempPath); + + using(ZipFile backupZipFile = new ZipFile(backupZipFileName)) { + + // Find all namespaces + List namespaces = new List(); + foreach(IPagesStorageProviderV30 pagesStorageProvider in pagesStorageProviders) { + foreach(NamespaceInfo ns in pagesStorageProvider.GetNamespaces()) { + namespaces.Add(ns.Name); + } + } + + // Backup settings storage provider + string zipSettingsBackup = Path.Combine(tempPath, "SettingsBackup-" + settingsStorageProvider.GetType().FullName + ".zip"); + BackupSettingsStorageProvider(zipSettingsBackup, settingsStorageProvider, namespaces.ToArray(), plugins); + backupZipFile.AddFile(zipSettingsBackup, ""); + + // Backup pages storage providers + foreach(IPagesStorageProviderV30 pagesStorageProvider in pagesStorageProviders) { + string zipPagesBackup = Path.Combine(tempPath, "PagesBackup-" + pagesStorageProvider.GetType().FullName + ".zip"); + BackupPagesStorageProvider(zipPagesBackup, pagesStorageProvider); + backupZipFile.AddFile(zipPagesBackup, ""); + } + + // Backup users storage providers + foreach(IUsersStorageProviderV30 usersStorageProvider in usersStorageProviders) { + string zipUsersProvidersBackup = Path.Combine(tempPath, "UsersBackup-" + usersStorageProvider.GetType().FullName + ".zip"); + BackupUsersStorageProvider(zipUsersProvidersBackup, usersStorageProvider); + backupZipFile.AddFile(zipUsersProvidersBackup, ""); + } + + // Backup files storage providers + foreach(IFilesStorageProviderV30 filesStorageProvider in filesStorageProviders) { + string zipFilesProviderBackup = Path.Combine(tempPath, "FilesBackup-" + filesStorageProvider.GetType().FullName + ".zip"); + BackupFilesStorageProvider(zipFilesProviderBackup, filesStorageProvider, pagesStorageProviders); + backupZipFile.AddFile(zipFilesProviderBackup, ""); + } + backupZipFile.Save(); + } + + Directory.Delete(tempPath, true); + return true; + } + + /// + /// Backups the specified settings provider. + /// + /// The zip file name where to store the backup. + /// The source settings provider. + /// The currently known page namespaces. + /// The currently known plugins. + /// true if the backup file has been succesfully created. + public static bool BackupSettingsStorageProvider(string zipFileName, ISettingsStorageProviderV30 settingsStorageProvider, string[] knownNamespaces, string[] knownPlugins) { + SettingsBackup settingsBackup = new SettingsBackup(); + + // Settings + settingsBackup.Settings = (Dictionary)settingsStorageProvider.GetAllSettings(); + + // Plugins Status and Configuration + Dictionary pluginsStatus = new Dictionary(); + Dictionary pluginsConfiguration = new Dictionary(); + foreach(string plugin in knownPlugins) { + pluginsStatus[plugin] = settingsStorageProvider.GetPluginStatus(plugin); + pluginsConfiguration[plugin] = settingsStorageProvider.GetPluginConfiguration(plugin); + } + settingsBackup.PluginsStatus = pluginsStatus; + settingsBackup.PluginsConfiguration = pluginsConfiguration; + + // Metadata + List metadataList = new List(); + // Meta-data (global) + metadataList.Add(new MetaData() { + Item = MetaDataItem.AccountActivationMessage, + Tag = null, + Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.AccountActivationMessage, null) + }); + metadataList.Add(new MetaData() { Item = MetaDataItem.PasswordResetProcedureMessage, Tag = null, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.PasswordResetProcedureMessage, null) }); + metadataList.Add(new MetaData() { Item = MetaDataItem.LoginNotice, Tag = null, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.LoginNotice, null) }); + metadataList.Add(new MetaData() { Item = MetaDataItem.PageChangeMessage, Tag = null, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.PageChangeMessage, null) }); + metadataList.Add(new MetaData() { Item = MetaDataItem.DiscussionChangeMessage, Tag = null, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.DiscussionChangeMessage, null) }); + // Meta-data (ns-specific) + List namespacesToProcess = new List(); + namespacesToProcess.Add(""); + namespacesToProcess.AddRange(knownNamespaces); + foreach(string nspace in namespacesToProcess) { + metadataList.Add(new MetaData() { Item = MetaDataItem.EditNotice, Tag = nspace, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.EditNotice, nspace) }); + metadataList.Add(new MetaData() { Item = MetaDataItem.Footer, Tag = nspace, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.Footer, nspace) }); + metadataList.Add(new MetaData() { Item = MetaDataItem.Header, Tag = nspace, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.Header, nspace) }); + metadataList.Add(new MetaData() { Item = MetaDataItem.HtmlHead, Tag = nspace, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.HtmlHead, nspace) }); + metadataList.Add(new MetaData() { Item = MetaDataItem.PageFooter, Tag = nspace, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.PageFooter, nspace) }); + metadataList.Add(new MetaData() { Item = MetaDataItem.PageHeader, Tag = nspace, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.PageHeader, nspace) }); + metadataList.Add(new MetaData() { Item = MetaDataItem.Sidebar, Tag = nspace, Content = settingsStorageProvider.GetMetaDataItem(MetaDataItem.Sidebar, nspace) }); + } + settingsBackup.Metadata = metadataList; + + // RecentChanges + settingsBackup.RecentChanges = settingsStorageProvider.GetRecentChanges().ToList(); + + // OutgoingLinks + settingsBackup.OutgoingLinks = (Dictionary)settingsStorageProvider.GetAllOutgoingLinks(); + + // ACLEntries + AclEntry[] aclEntries = settingsStorageProvider.AclManager.RetrieveAllEntries(); + settingsBackup.AclEntries = new List(aclEntries.Length); + foreach(AclEntry aclEntry in aclEntries) { + settingsBackup.AclEntries.Add(new AclEntryBackup() { + Action = aclEntry.Action, + Resource = aclEntry.Resource, + Subject = aclEntry.Subject, + Value = aclEntry.Value + }); + } + + JavaScriptSerializer javascriptSerializer = new JavaScriptSerializer(); + javascriptSerializer.MaxJsonLength = javascriptSerializer.MaxJsonLength * 10; + + string tempDir = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + + FileStream tempFile = File.Create(Path.Combine(tempDir, "Settings.json")); + byte[] buffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(settingsBackup)); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + + tempFile = File.Create(Path.Combine(tempDir, "Version.json")); + buffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(generateVersionFile("Settings"))); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + + List plugins = settingsStorageProvider.ListPluginAssemblies().ToList(); + foreach(string pluginFileName in plugins) { + tempFile = File.Create(Path.Combine(tempDir, pluginFileName)); + buffer = settingsStorageProvider.RetrievePluginAssembly(pluginFileName); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + } + + using(ZipFile zipFile = new ZipFile()) { + zipFile.AddDirectory(tempDir, ""); + zipFile.Save(zipFileName); + } + Directory.Delete(tempDir, true); + + return true; + } + + /// + /// Backups the pages storage provider. + /// + /// The zip file name where to store the backup. + /// The pages storage provider. + /// true if the backup file has been succesfully created. + public static bool BackupPagesStorageProvider(string zipFileName, IPagesStorageProviderV30 pagesStorageProvider) { + JavaScriptSerializer javascriptSerializer = new JavaScriptSerializer(); + javascriptSerializer.MaxJsonLength = javascriptSerializer.MaxJsonLength * 10; + + string tempDir = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + + List nspaces = new List(pagesStorageProvider.GetNamespaces()); + nspaces.Add(null); + List namespaceBackupList = new List(nspaces.Count); + foreach(NamespaceInfo nspace in nspaces) { + + // Backup categories + CategoryInfo[] categories = pagesStorageProvider.GetCategories(nspace); + List categoriesBackup = new List(categories.Length); + foreach(CategoryInfo category in categories) { + // Add this category to the categoriesBackup list + categoriesBackup.Add(new CategoryBackup() { + FullName = category.FullName, + Pages = category.Pages + }); + } + + // Backup NavigationPaths + NavigationPath[] navigationPaths = pagesStorageProvider.GetNavigationPaths(nspace); + List navigationPathsBackup = new List(navigationPaths.Length); + foreach(NavigationPath navigationPath in navigationPaths) { + navigationPathsBackup.Add(new NavigationPathBackup() { + FullName = navigationPath.FullName, + Pages = navigationPath.Pages + }); + } + + // Add this namespace to the namespaceBackup list + namespaceBackupList.Add(new NamespaceBackup() { + Name = nspace == null ? "" : nspace.Name, + DefaultPageFullName = nspace == null ? "" : nspace.DefaultPage.FullName, + Categories = categoriesBackup, + NavigationPaths = navigationPathsBackup + }); + + // Backup pages (one json file for each page containing a maximum of 100 revisions) + PageInfo[] pages = pagesStorageProvider.GetPages(nspace); + foreach(PageInfo page in pages) { + PageContent pageContent = pagesStorageProvider.GetContent(page); + PageBackup pageBackup = new PageBackup(); + pageBackup.FullName = page.FullName; + pageBackup.CreationDateTime = page.CreationDateTime; + pageBackup.LastModified = pageContent.LastModified; + pageBackup.Content = pageContent.Content; + pageBackup.Comment = pageContent.Comment; + pageBackup.Description = pageContent.Description; + pageBackup.Keywords = pageContent.Keywords; + pageBackup.Title = pageContent.Title; + pageBackup.User = pageContent.User; + pageBackup.LinkedPages = pageContent.LinkedPages; + pageBackup.Categories = (from c in pagesStorageProvider.GetCategoriesForPage(page) + select c.FullName).ToArray(); + + // Backup the 100 most recent versions of the page + List pageContentBackupList = new List(); + int[] revisions = pagesStorageProvider.GetBackups(page); + for(int i = 0; i < Math.Min(revisions.Length, 100); i++) { + PageContent pageRevision = pagesStorageProvider.GetBackupContent(page, revisions[i]); + PageRevisionBackup pageContentBackup = new PageRevisionBackup() { + Revision = revisions[i], + Content = pageRevision.Content, + Comment = pageRevision.Comment, + Description = pageRevision.Description, + Keywords = pageRevision.Keywords, + Title = pageRevision.Title, + User = pageRevision.User, + LastModified = pageRevision.LastModified + }; + pageContentBackupList.Add(pageContentBackup); + } + pageBackup.Revisions = pageContentBackupList; + + // Backup draft of the page + PageContent draft = pagesStorageProvider.GetDraft(page); + if(draft != null) { + pageBackup.Draft = new PageRevisionBackup() { + Content = draft.Content, + Comment = draft.Comment, + Description = draft.Description, + Keywords = draft.Keywords, + Title = draft.Title, + User = draft.User, + LastModified = draft.LastModified + }; + } + + // Backup all messages of the page + List messageBackupList = new List(); + foreach(Message message in pagesStorageProvider.GetMessages(page)) { + messageBackupList.Add(BackupMessage(message)); + } + pageBackup.Messages = messageBackupList; + + FileStream tempFile = File.Create(Path.Combine(tempDir, page.FullName + ".json")); + byte[] buffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(pageBackup)); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + } + } + FileStream tempNamespacesFile = File.Create(Path.Combine(tempDir, "Namespaces.json")); + byte[] namespacesBuffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(namespaceBackupList)); + tempNamespacesFile.Write(namespacesBuffer, 0, namespacesBuffer.Length); + tempNamespacesFile.Close(); + + // Backup content templates + ContentTemplate[] contentTemplates = pagesStorageProvider.GetContentTemplates(); + List contentTemplatesBackup = new List(contentTemplates.Length); + foreach(ContentTemplate contentTemplate in contentTemplates) { + contentTemplatesBackup.Add(new ContentTemplateBackup() { + Name = contentTemplate.Name, + Content = contentTemplate.Content + }); + } + FileStream tempContentTemplatesFile = File.Create(Path.Combine(tempDir, "ContentTemplates.json")); + byte[] contentTemplateBuffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(contentTemplatesBackup)); + tempContentTemplatesFile.Write(contentTemplateBuffer, 0, contentTemplateBuffer.Length); + tempContentTemplatesFile.Close(); + + // Backup Snippets + Snippet[] snippets = pagesStorageProvider.GetSnippets(); + List snippetsBackup = new List(snippets.Length); + foreach(Snippet snippet in snippets) { + snippetsBackup.Add(new SnippetBackup() { + Name = snippet.Name, + Content = snippet.Content + }); + } + FileStream tempSnippetsFile = File.Create(Path.Combine(tempDir, "Snippets.json")); + byte[] snippetBuffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(snippetsBackup)); + tempSnippetsFile.Write(snippetBuffer, 0, snippetBuffer.Length); + tempSnippetsFile.Close(); + + FileStream tempVersionFile = File.Create(Path.Combine(tempDir, "Version.json")); + byte[] versionBuffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(generateVersionFile("Pages"))); + tempVersionFile.Write(versionBuffer, 0, versionBuffer.Length); + tempVersionFile.Close(); + + using(ZipFile zipFile = new ZipFile()) { + zipFile.AddDirectory(tempDir, ""); + zipFile.Save(zipFileName); + } + Directory.Delete(tempDir, true); + + return true; + } + + // Backup a message with a recursive function to backup all its replies. + private static MessageBackup BackupMessage(Message message) { + MessageBackup messageBackup = new MessageBackup() { + Id = message.ID, + Subject = message.Subject, + Body = message.Body, + DateTime = message.DateTime, + Username = message.Username + }; + List repliesBackup = new List(message.Replies.Length); + foreach(Message reply in message.Replies) { + repliesBackup.Add(BackupMessage(reply)); + } + messageBackup.Replies = repliesBackup; + return messageBackup; + } + + /// + /// Backups the users storage provider. + /// + /// The zip file name where to store the backup. + /// The users storage provider. + /// true if the backup file has been succesfully created. + public static bool BackupUsersStorageProvider(string zipFileName, IUsersStorageProviderV30 usersStorageProvider) { + JavaScriptSerializer javascriptSerializer = new JavaScriptSerializer(); + javascriptSerializer.MaxJsonLength = javascriptSerializer.MaxJsonLength * 10; + + string tempDir = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + + // Backup users + UserInfo[] users = usersStorageProvider.GetUsers(); + List usersBackup = new List(users.Length); + foreach(UserInfo user in users) { + usersBackup.Add(new UserBackup() { + Username = user.Username, + Active = user.Active, + DateTime = user.DateTime, + DisplayName = user.DisplayName, + Email = user.Email, + Groups = user.Groups, + UserData = usersStorageProvider.RetrieveAllUserData(user) + }); + } + FileStream tempFile = File.Create(Path.Combine(tempDir, "Users.json")); + byte[] buffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(usersBackup)); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + + // Backup UserGroups + UserGroup[] userGroups = usersStorageProvider.GetUserGroups(); + List userGroupsBackup = new List(userGroups.Length); + foreach(UserGroup userGroup in userGroups) { + userGroupsBackup.Add(new UserGroupBackup() { + Name = userGroup.Name, + Description = userGroup.Description + }); + } + + tempFile = File.Create(Path.Combine(tempDir, "Groups.json")); + buffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(userGroupsBackup)); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + + tempFile = File.Create(Path.Combine(tempDir, "Version.json")); + buffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(generateVersionFile("Users"))); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + + + using(ZipFile zipFile = new ZipFile()) { + zipFile.AddDirectory(tempDir, ""); + zipFile.Save(zipFileName); + } + Directory.Delete(tempDir, true); + + return true; + } + + /// + /// Backups the files storage provider. + /// + /// The zip file name where to store the backup. + /// The files storage provider. + /// The pages storage providers. + /// true if the backup file has been succesfully created. + public static bool BackupFilesStorageProvider(string zipFileName, IFilesStorageProviderV30 filesStorageProvider, IPagesStorageProviderV30[] pagesStorageProviders) { + JavaScriptSerializer javascriptSerializer = new JavaScriptSerializer(); + javascriptSerializer.MaxJsonLength = javascriptSerializer.MaxJsonLength * 10; + + string tempDir = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + + DirectoryBackup directoriesBackup = BackupDirectory(filesStorageProvider, tempDir, null); + FileStream tempFile = File.Create(Path.Combine(tempDir, "Files.json")); + byte[] buffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(directoriesBackup)); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + + + // Backup Pages Attachments + string[] pagesWithAttachment = filesStorageProvider.GetPagesWithAttachments(); + foreach(string pageWithAttachment in pagesWithAttachment) { + PageInfo pageInfo = FindPageInfo(pageWithAttachment, pagesStorageProviders); + if(pageInfo != null) { + string[] attachments = filesStorageProvider.ListPageAttachments(pageInfo); + List attachmentsBackup = new List(attachments.Length); + foreach(string attachment in attachments) { + FileDetails attachmentDetails = filesStorageProvider.GetPageAttachmentDetails(pageInfo, attachment); + attachmentsBackup.Add(new AttachmentBackup() { + Name = attachment, + PageFullName = pageWithAttachment, + LastModified = attachmentDetails.LastModified, + Size = attachmentDetails.Size + }); + using(MemoryStream stream = new MemoryStream()) { + filesStorageProvider.RetrievePageAttachment(pageInfo, attachment, stream, false); + stream.Seek(0, SeekOrigin.Begin); + byte[] tempBuffer = new byte[stream.Length]; + stream.Read(tempBuffer, 0, (int)stream.Length); + + DirectoryInfo dir = Directory.CreateDirectory(Path.Combine(tempDir, Path.Combine("__attachments", pageInfo.FullName))); + tempFile = File.Create(Path.Combine(dir.FullName, attachment)); + tempFile.Write(tempBuffer, 0, tempBuffer.Length); + tempFile.Close(); + } + } + tempFile = File.Create(Path.Combine(tempDir, Path.Combine("__attachments", Path.Combine(pageInfo.FullName, "Attachments.json")))); + buffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(attachmentsBackup)); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + } + } + + tempFile = File.Create(Path.Combine(tempDir, "Version.json")); + buffer = Encoding.Unicode.GetBytes(javascriptSerializer.Serialize(generateVersionFile("Files"))); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + + using(ZipFile zipFile = new ZipFile()) { + zipFile.AddDirectory(tempDir, ""); + zipFile.Save(zipFileName); + } + Directory.Delete(tempDir, true); + + return true; + } + + private static PageInfo FindPageInfo(string pageWithAttachment, IPagesStorageProviderV30[] pagesStorageProviders) { + foreach(IPagesStorageProviderV30 pagesStorageProvider in pagesStorageProviders) { + PageInfo pageInfo = pagesStorageProvider.GetPage(pageWithAttachment); + if(pageInfo != null) return pageInfo; + } + return null; + } + + private static DirectoryBackup BackupDirectory(IFilesStorageProviderV30 filesStorageProvider, string zipFileName, string directory) { + DirectoryBackup directoryBackup = new DirectoryBackup(); + + string[] files = filesStorageProvider.ListFiles(directory); + List filesBackup = new List(files.Length); + foreach(string file in files) { + FileDetails fileDetails = filesStorageProvider.GetFileDetails(file); + filesBackup.Add(new FileBackup() { + Name = file, + Size = fileDetails.Size, + LastModified = fileDetails.LastModified + }); + + FileStream tempFile = File.Create(Path.Combine(zipFileName.Trim('/').Trim('\\'), file.Trim('/').Trim('\\'))); + using(MemoryStream stream = new MemoryStream()) { + filesStorageProvider.RetrieveFile(file, stream, false); + stream.Seek(0, SeekOrigin.Begin); + byte[] buffer = new byte[stream.Length]; + stream.Read(buffer, 0, buffer.Length); + tempFile.Write(buffer, 0, buffer.Length); + tempFile.Close(); + } + } + directoryBackup.Name = directory; + directoryBackup.Files = filesBackup; + + string[] directories = filesStorageProvider.ListDirectories(directory); + List subdirectoriesBackup = new List(directories.Length); + foreach(string d in directories) { + subdirectoriesBackup.Add(BackupDirectory(filesStorageProvider, zipFileName, d)); + } + directoryBackup.SubDirectories = subdirectoriesBackup; + + return directoryBackup; + } + + } + + internal class SettingsBackup { + public Dictionary Settings { get; set; } + public Dictionary PluginsStatus { get; set; } + public Dictionary PluginsConfiguration { get; set; } + public List Metadata { get; set; } + public List RecentChanges { get; set; } + public Dictionary OutgoingLinks { get; set; } + public List AclEntries { get; set; } + } + + internal class AclEntryBackup { + public Value Value { get; set; } + public string Subject { get; set; } + public string Resource { get; set; } + public string Action { get; set; } + } + + internal class MetaData { + public MetaDataItem Item {get; set;} + public string Tag {get; set;} + public string Content {get; set;} + } + + internal class GlobalSettingsBackup { + public Dictionary Settings { get; set; } + public List pluginsFileNames { get; set; } + } + + internal class PageBackup { + public String FullName { get; set; } + public DateTime CreationDateTime { get; set; } + public DateTime LastModified { get; set; } + public string Content { get; set; } + public string Comment { get; set; } + public string Description { get; set; } + public string[] Keywords { get; set; } + public string Title { get; set; } + public string User { get; set; } + public string[] LinkedPages { get; set; } + public List Revisions { get; set; } + public PageRevisionBackup Draft { get; set; } + public List Messages { get; set; } + public string[] Categories { get; set; } + } + + internal class PageRevisionBackup { + public string Content { get; set; } + public string Comment { get; set; } + public string Description { get; set; } + public string[] Keywords { get; set; } + public string Title { get; set; } + public string User { get; set; } + public DateTime LastModified { get; set; } + public int Revision { get; set; } + } + + internal class NamespaceBackup { + public string Name { get; set; } + public string DefaultPageFullName { get; set; } + public List Categories { get; set; } + public List NavigationPaths { get; set; } + } + + internal class CategoryBackup { + public string FullName { get; set; } + public string[] Pages { get; set; } + } + + internal class ContentTemplateBackup { + public string Name { get; set; } + public string Content { get; set; } + } + + internal class MessageBackup { + public List Replies { get; set; } + public int Id { get; set; } + public string Subject { get; set; } + public string Body { get; set; } + public DateTime DateTime { get; set; } + public string Username { get; set; } + } + + internal class NavigationPathBackup { + public string FullName { get; set; } + public string[] Pages { get; set; } + } + + internal class SnippetBackup { + public string Name { get; set; } + public string Content { get; set; } + } + + internal class UserBackup { + public string Username { get; set; } + public bool Active { get; set; } + public DateTime DateTime { get; set; } + public string DisplayName { get; set; } + public string Email { get; set; } + public string[] Groups { get; set; } + public IDictionary UserData { get; set; } + } + + internal class UserGroupBackup { + public string Name { get; set; } + public string Description { get; set; } + } + + internal class DirectoryBackup { + public List Files { get; set; } + public List SubDirectories { get; set; } + public string Name { get; set; } + } + + internal class FileBackup { + public string Name { get; set; } + public long Size { get; set; } + public DateTime LastModified { get; set; } + public string DirectoryName { get; set; } + } + + internal class VersionFile { + public string BackupRestoreVersion { get; set; } + public string WikiVersion { get; set; } + public string BackupName { get; set; } + } + + internal class AttachmentBackup { + public string Name { get; set; } + public string PageFullName { get; set; } + public DateTime LastModified { get; set; } + public long Size { get; set; } + } + +} diff --git a/BackupRestore/BackupRestore.csproj b/BackupRestore/BackupRestore.csproj new file mode 100644 index 0000000..fee4430 --- /dev/null +++ b/BackupRestore/BackupRestore.csproj @@ -0,0 +1,76 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {06320590-F6FC-4F8B-B3A2-2B5991676BE8} + Library + Properties + ScrewTurn.Wiki.BackupRestore + ScrewTurn.Wiki.BackupRestore + v3.5 + 512 + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + bin\Debug\ScrewTurn.Wiki.BackupRestore.XML + + + none + true + bin\Release\ + TRACE + prompt + 4 + true + bin\Release\ScrewTurn.Wiki.BackupRestore.XML + + + + ..\References\Lib\DotNetZipLib-DevKit-v1.9\Ionic.Zip.Reduced.dll + + + + + + + + + + + + + AssemblyVersion.cs + + + + + + + {44B0F4C1-8CDC-4272-B2A2-C0AF689CEB81} + AclEngine + + + {531A83D6-76F9-4014-91C5-295818E2D948} + PluginFramework + + + + + \ No newline at end of file diff --git a/BackupRestore/Properties/AssemblyInfo.cs b/BackupRestore/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..f9e9ec6 --- /dev/null +++ b/BackupRestore/Properties/AssemblyInfo.cs @@ -0,0 +1,18 @@ +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("ScrewTurn Wiki Backup Restore")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] + +// 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("b06902e4-3d04-4c40-928c-862bdcd83815")] diff --git a/References/Lib/DotNetZipLib-DevKit-v1.9/Ionic.Zip.Reduced.dll b/References/Lib/DotNetZipLib-DevKit-v1.9/Ionic.Zip.Reduced.dll new file mode 100644 index 0000000..9622cc5 Binary files /dev/null and b/References/Lib/DotNetZipLib-DevKit-v1.9/Ionic.Zip.Reduced.dll differ diff --git a/References/Lib/DotNetZipLib-DevKit-v1.9/License.txt b/References/Lib/DotNetZipLib-DevKit-v1.9/License.txt new file mode 100644 index 0000000..e370555 --- /dev/null +++ b/References/Lib/DotNetZipLib-DevKit-v1.9/License.txt @@ -0,0 +1,33 @@ +Microsoft Public License (Ms-PL) + +This license governs use of the accompanying software, the DotNetZip library ("the software"). If you use the software, you accept this license. If you do not accept the license, do not use the software. + +1. Definitions + +The terms "reproduce," "reproduction," "derivative works," and "distribution" have the same meaning here as under U.S. copyright law. + +A "contribution" is the original software, or any additions or changes to the software. + +A "contributor" is any person that distributes its contribution under this license. + +"Licensed patents" are a contributor's patent claims that read directly on its contribution. + +2. Grant of Rights + +(A) Copyright Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free copyright license to reproduce its contribution, prepare derivative works of its contribution, and distribute its contribution or any derivative works that you create. + +(B) Patent Grant- Subject to the terms of this license, including the license conditions and limitations in section 3, each contributor grants you a non-exclusive, worldwide, royalty-free license under its licensed patents to make, have made, use, sell, offer for sale, import, and/or otherwise dispose of its contribution in the software or derivative works of the contribution in the software. + +3. Conditions and Limitations + +(A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks. + +(B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically. + +(C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software. + +(D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license. + +(E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. + + diff --git a/ScrewTurnWiki.sln b/ScrewTurnWiki.sln index 43d8f0b..e2985de 100644 --- a/ScrewTurnWiki.sln +++ b/ScrewTurnWiki.sln @@ -67,6 +67,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnfuddleTicketsPlugin", "Un EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RatingManagerPlugin", "RatingManagerPlugin\RatingManagerPlugin.csproj", "{B65C793F-62C4-4B81-95C4-3E4805E80411}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BackupRestore", "BackupRestore\BackupRestore.csproj", "{06320590-F6FC-4F8B-B3A2-2B5991676BE8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -161,6 +163,10 @@ Global {B65C793F-62C4-4B81-95C4-3E4805E80411}.Debug|Any CPU.Build.0 = Debug|Any CPU {B65C793F-62C4-4B81-95C4-3E4805E80411}.Release|Any CPU.ActiveCfg = Release|Any CPU {B65C793F-62C4-4B81-95C4-3E4805E80411}.Release|Any CPU.Build.0 = Release|Any CPU + {06320590-F6FC-4F8B-B3A2-2B5991676BE8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {06320590-F6FC-4F8B-B3A2-2B5991676BE8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {06320590-F6FC-4F8B-B3A2-2B5991676BE8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {06320590-F6FC-4F8B-B3A2-2B5991676BE8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WebApplication/AdminProviders.aspx b/WebApplication/AdminProviders.aspx index 2402e42..56d3b6e 100644 --- a/WebApplication/AdminProviders.aspx +++ b/WebApplication/AdminProviders.aspx @@ -249,6 +249,9 @@ +

+ +
diff --git a/WebApplication/AdminProviders.aspx.cs b/WebApplication/AdminProviders.aspx.cs index 2d3a7af..981b7f8 100644 --- a/WebApplication/AdminProviders.aspx.cs +++ b/WebApplication/AdminProviders.aspx.cs @@ -6,6 +6,8 @@ using System.Web.UI; using System.Web.UI.WebControls; using ScrewTurn.Wiki.PluginFramework; using System.Net; +using System.Linq; +using System.IO; namespace ScrewTurn.Wiki { @@ -538,6 +540,38 @@ namespace ScrewTurn.Wiki { #endregion + protected void btnExport_Click(object sender, EventArgs e) { + Log.LogEntry("Data export requested.", EntryType.General, SessionFacade.GetCurrentUsername()); + + string tempDir = Path.Combine(Environment.GetEnvironmentVariable("TEMP"), Guid.NewGuid().ToString()); + Directory.CreateDirectory(tempDir); + string zipFileName = Path.Combine(tempDir, "Backup.zip"); + + bool backupFileSucceded = BackupRestore.BackupRestore.BackupAll(zipFileName, Settings.Provider.ListPluginAssemblies(), + Settings.Provider, + (from p in Collectors.PagesProviderCollector.AllProviders where !p.ReadOnly select p).ToArray(), + (from p in Collectors.UsersProviderCollector.AllProviders where IsUsersProviderFullWriteEnabled(p) select p).ToArray(), + (from p in Collectors.FilesProviderCollector.AllProviders where !p.ReadOnly select p).ToArray()); + + FileInfo file = new FileInfo(zipFileName); + Response.Clear(); + Response.AddHeader("content-type", GetMimeType(zipFileName)); + Response.AddHeader("content-disposition", "attachment;filename=Backup.zip"); + Response.AddHeader("content-length", file.Length.ToString()); + + Response.TransmitFile(zipFileName); + Response.Flush(); + + Directory.Delete(tempDir, true); + Log.LogEntry("Data export completed.", EntryType.General, SessionFacade.GetCurrentUsername()); + } + + private string GetMimeType(string ext) { + string mime = ""; + if(MimeTypes.Types.TryGetValue(ext, out mime)) return mime; + else return "application/octet-stream"; + } + /// /// Detects whether a users storage provider fully supports writing to all managed data. /// diff --git a/WebApplication/AdminProviders.aspx.designer.cs b/WebApplication/AdminProviders.aspx.designer.cs index 54a2752..c44b901 100644 --- a/WebApplication/AdminProviders.aspx.designer.cs +++ b/WebApplication/AdminProviders.aspx.designer.cs @@ -1,10 +1,9 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:2.0.50727.4927 // // Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -660,5 +659,14 @@ namespace ScrewTurn.Wiki { /// To modify move field declaration from designer file to code-behind file. /// protected global::Anthem.TextBox txtSettingsDestinationConfig; + + /// + /// btnExport control. + /// + /// + /// Auto-generated field. + /// To modify move field declaration from designer file to code-behind file. + /// + protected global::System.Web.UI.WebControls.Button btnExport; } } diff --git a/WebApplication/WebApplication.csproj b/WebApplication/WebApplication.csproj index 2cea7eb..ba969a3 100644 --- a/WebApplication/WebApplication.csproj +++ b/WebApplication/WebApplication.csproj @@ -2676,6 +2676,10 @@ {44B0F4C1-8CDC-4272-B2A2-C0AF689CEB81} AclEngine + + {06320590-F6FC-4F8B-B3A2-2B5991676BE8} + BackupRestore + {C353A35C-86D0-4154-9500-4F88CAAB29C3} Core @@ -2796,7 +2800,7 @@ False True - 52149 + 10043 /