diff --git a/src/managed/OpenLiveWriter.BlogClient/Clients/StaticSiteClient.cs b/src/managed/OpenLiveWriter.BlogClient/Clients/StaticSiteClient.cs new file mode 100644 index 00000000..576b0f27 --- /dev/null +++ b/src/managed/OpenLiveWriter.BlogClient/Clients/StaticSiteClient.cs @@ -0,0 +1,207 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading.Tasks; +using System.Xml; +using OpenLiveWriter.BlogClient.Providers; +using OpenLiveWriter.CoreServices; +using OpenLiveWriter.Extensibility.BlogClient; +using YamlDotNet.Serialization; + +namespace OpenLiveWriter.BlogClient.Clients +{ + [BlogClient(StaticSiteClient.CLIENT_TYPE, StaticSiteClient.CLIENT_TYPE)] + public class StaticSiteClient : BlogClientBase, IBlogClient + { + // The 'provider' concept doesn't really apply to local static sites + // Store these required constants here so they're in one place + public const string PROVIDER_ID = "D0E0062F-7540-4462-94FD-DC55004D95E6"; + public const string SERVICE_NAME = "Static Site Generator"; // TODO- Move to Strings + public const string POST_API_URL = "http://localhost/"; // A valid URI is required for BlogClientManager to instantiate a URI object on. + public const string CLIENT_TYPE = "StaticSite"; + + // The credential keys where the configuration is stored. + // Used in WeblogConfigurationWizardPanelStaticSiteConfig + public const string CONFIG_POSTS_PATH = "SSGPostsPath"; + public const string CONFIG_PAGES_PATH = "SSGPagesPath"; + public const string CONFIG_BUILD_COMMAND = "SSGBuildCommand"; + public const string CONFIG_PUBLISH_COMMAND = "SSGPublishCommand"; + + public IBlogClientOptions Options { get; private set; } + + // Configuration + public string LocalSitePath { get; private set; } + public string PostsPath { get; private set; } + public string PagesPath { get; private set; } + public string BuildCommand { get; private set; } + public string PublishCommand { get; private set; } + + public StaticSiteClient(Uri postApiUrl, IBlogCredentialsAccessor credentials) + : base(credentials) + { + LoadConfigFromCredentials(credentials); + + // Set the client options + var options = new BlogClientOptions(); + ConfigureClientOptions(options); + Options = options; + } + + protected override void VerifyCredentials(TransientCredentials transientCredentials) + { + + } + + public void OverrideOptions(IBlogClientOptions newClientOptions) + { + Options = newClientOptions; + } + + public BlogInfo[] GetUsersBlogs() => new BlogInfo[0]; + + public BlogPostCategory[] GetCategories(string blogId) => new BlogPostCategory[0]; + public BlogPostKeyword[] GetKeywords(string blogId) => new BlogPostKeyword[0]; + + /// + /// Returns recent posts + /// + /// + /// + /// + /// If null, then includes future posts. If non-null, then only includes posts before the *UTC* 'now' time. + /// + public BlogPost[] GetRecentPosts(string blogId, int maxPosts, bool includeCategories, DateTime? now) => new BlogPost[0]; + + public string NewPost(string blogId, BlogPost post, INewCategoryContext newCategoryContext, bool publish, out string etag, out XmlDocument remotePost) + { + etag = ""; + remotePost = new XmlDocument(); + return ""; + } + + public bool EditPost(string blogId, BlogPost post, INewCategoryContext newCategoryContext, bool publish, out string etag, out XmlDocument remotePost) + { + etag = ""; + remotePost = new XmlDocument(); + return false; + } + + /// + /// Attempt to get a post with the specified id (note: may return null + /// if the post could not be found on the remote server) + /// + public BlogPost GetPost(string blogId, string postId) => new BlogPost(); + + public void DeletePost(string blogId, string postId, bool publish) + { + } + + public BlogPost GetPage(string blogId, string pageId) => new BlogPost(); + public PageInfo[] GetPageList(string blogId) => new PageInfo[0]; + public BlogPost[] GetPages(string blogId, int maxPages) => new BlogPost[0]; + + public string NewPage(string blogId, BlogPost page, bool publish, out string etag, out XmlDocument remotePost) + { + etag = ""; + remotePost = new XmlDocument(); + return ""; + } + + public bool EditPage(string blogId, BlogPost page, bool publish, out string etag, out XmlDocument remotePost) + { + etag = ""; + remotePost = new XmlDocument(); + return false; + } + + public void DeletePage(string blogId, string pageId) + { + } + + public AuthorInfo[] GetAuthors(string blogId) => new AuthorInfo[0]; + public bool? DoesFileNeedUpload(IFileUploadContext uploadContext) => false; + public string DoBeforePublishUploadWork(IFileUploadContext uploadContext) => ""; + + public void DoAfterPublishUploadWork(IFileUploadContext uploadContext) + { + } + + public string AddCategory(string blogId, BlogPostCategory category) => ""; + + public BlogPostCategory[] SuggestCategories(string blogId, string partialCategoryName) + => new BlogPostCategory[0]; + + public HttpWebResponse SendAuthenticatedHttpRequest(string requestUri, int timeoutMs, HttpRequestFilter filter) + => throw new Exception("HTTP requests not implemented for static sites"); // TODO This is used for downloading writing manifest XMLs. Throw an exception for now. + + public BlogInfo[] GetImageEndpoints() => new BlogInfo[0]; + + /// + /// Returns if this StaticSiteGeneratorClient is secure + /// Returns true for now as we trust the user publish script + /// + public bool IsSecure => true; + + // Authentication is handled by publish script at the moment + protected override bool RequiresPassword => false; + + /// + /// Sets the relevant BlogClientOptions for this client + /// + /// A BlogClientOptions instance + private void ConfigureClientOptions(BlogClientOptions clientOptions) + { + // Pages are supported via filesystem + clientOptions.SupportsPages = true; + + // The follwoing values would be written into YAML front-matter + clientOptions.SupportsCategories = true; + clientOptions.SupportsMultipleCategories = true; + clientOptions.SupportsNewCategories = true; + clientOptions.SupportsCustomDate = true; + clientOptions.SupportsFileUpload = true; + clientOptions.SupportsSlug = true; + clientOptions.SupportsAuthor = true; + } + + /// + /// Load client configuration from blog credentials + /// + /// An IBlogCredentialsAccessor + private void LoadConfigFromCredentials(IBlogCredentialsAccessor blogCredentials) + { + LocalSitePath = blogCredentials.Username; + PostsPath = blogCredentials.GetCustomValue(CONFIG_POSTS_PATH); + PagesPath = blogCredentials.GetCustomValue(CONFIG_PAGES_PATH); + BuildCommand = blogCredentials.GetCustomValue(CONFIG_BUILD_COMMAND); + PublishCommand = blogCredentials.GetCustomValue(CONFIG_PUBLISH_COMMAND); + } + + /// + /// Get a PostFrontMatter instance for a post + /// + /// Post to generate front matter for + /// + private PostFrontMatter GetFrontMatterForPost(BlogPost post) => + new PostFrontMatter() + { + title = post.Title, + author = post.Author.Name, + date = post.DatePublished.ToString("yyyy-MM-dd HH:mm:ss"), + categories = post.Categories.Select(cat => cat.Name).ToArray(), + tags = post.Keywords + }; + + private class PostFrontMatter + { + public string title { get; set; } + public string author { get; set; } + public string date { get; set; } + + public string[] categories { get; set; } + public string tags { get; set; } + } + } +}