private void SavePostCustomFieldsToMetadata(Post post, WeblogPostMetadata meta) { if (post.CustomFields != null) { if (meta.CustomFields == null) { meta.CustomFields = new Dictionary <string, CustomField>(); } foreach (var cf in post.CustomFields) { if (cf.Key == "mt_markdown" || cf.Key == "wp_post_thumbnail") { // These fields have specific handling, we don't want to persist their value, // but we need to persist their ID. meta.CustomFields[cf.Key] = new CustomField { Id = cf.Id, Key = cf.Key }; } else { meta.CustomFields[cf.Key] = cf; } } } }
private void WebLogStart_Loaded(object sender, System.Windows.RoutedEventArgs e) { //Model.LoadWebLognames(); var editor = Model.AppModel.ActiveEditor; if (editor == null) { return; } var markdown = editor.GetMarkdown(); Model.ActivePostMetadata = WeblogPostMetadata.GetPostConfigFromMarkdown(markdown, Model.ActivePost, Model.ActiveWeblogInfo); Model.MetadataCustomFields = new ObservableCollection <CustomField>( Model.ActivePostMetadata.CustomFields.Select(kv => kv.Value)); var lastBlog = WeblogAddinConfiguration.Current.LastWeblogAccessed; if (string.IsNullOrEmpty(Model.ActivePostMetadata.WeblogName)) { Model.ActivePostMetadata.WeblogName = lastBlog; } // Code bindings ComboWeblogType.ItemsSource = Enum.GetValues(typeof(WeblogTypes)).Cast <WeblogTypes>(); // have to do this here otherwise MetadataCustomFields is not updating in model DataContext = Model; }
public string NewWeblogPost(WeblogPostMetadata meta) { if (meta == null) { meta = new WeblogPostMetadata() { Title = "Post Title", }; } if (string.IsNullOrEmpty(meta.WeblogName)) { meta.WeblogName = "Name of registered blog to post to"; } string post = $@"# {meta.Title} {meta.MarkdownBody} <!-- Post Configuration --> <!-- ```xml <blogpost> <title>{meta.Title}</title> <abstract> {meta.Abstract} </abstract> <categories> {meta.Categories} </categories> <isDraft>{meta.IsDraft}</isDraft> <featuredImage>{meta.FeaturedImageUrl}</featuredImage> <keywords> {meta.Keywords} </keywords> <weblogs> <postid>{meta.PostId}</postid> <weblog> {meta.WeblogName} </weblog> </weblogs> </blogpost> ``` --> <!-- End Post Configuration --> "; if (WeblogAddinConfiguration.Current.AddFrontMatterToNewBlogPost) { post = String.Format(WeblogAddinConfiguration.Current.FrontMatterTemplate, meta.Title, DateTime.Now) + post; } return(post); }
/// <summary> /// Strips the Markdown Meta data from the message and populates /// the post structure with the meta data values. /// </summary> /// <param name="markdown"></param> /// <returns></returns> public WeblogPostMetadata GetPostConfigFromMarkdown(string markdown) { var meta = new WeblogPostMetadata() { RawMarkdownBody = markdown, MarkdownBody = markdown }; string config = StringUtils.ExtractString(markdown, "<!-- Post Configuration -->", "<!-- End Post Configuration -->", caseSensitive: false, allowMissingEndDelimiter: true, returnDelimiters: true); if (string.IsNullOrEmpty(config)) { return(meta); } // strip the config section meta.MarkdownBody = meta.MarkdownBody.Replace(config, ""); // check for title in first line and remove it // since the body shouldn't render the title var lines = StringUtils.GetLines(markdown); if (lines.Length > 0 && lines[0].Trim().StartsWith("# ")) { meta.MarkdownBody = meta.MarkdownBody.Replace(lines[0], "").Trim(); meta.Title = lines[0].Trim().Replace("# ", ""); } if (string.IsNullOrEmpty(meta.Title)) { meta.Title = StringUtils.ExtractString(config, "\n<title>", "\n</title>").Trim(); } meta.Abstract = StringUtils.ExtractString(config, "\n<abstract>", "\n</abstract>").Trim(); meta.Keywords = StringUtils.ExtractString(config, "\n<keywords>", "\n</keywords>").Trim(); meta.Categories = StringUtils.ExtractString(config, "\n<categories>", "\n</categories>").Trim(); meta.PostId = StringUtils.ExtractString(config, "\n<postid>", "</postid>").Trim(); meta.WeblogName = StringUtils.ExtractString(config, "\n<weblog>", "</weblog>").Trim(); ActivePost.Title = meta.Title; ActivePost.Categories = meta.Categories.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); ActivePost.mt_excerpt = meta.Abstract; ActivePost.mt_keywords = meta.Keywords; return(meta); }
public string NewWeblogPost(WeblogPostMetadata meta) { if (meta == null) { meta = new WeblogPostMetadata() { Title = "Post Title", }; } if (string.IsNullOrEmpty(meta.WeblogName)) { meta.WeblogName = "Name of registered blog to post to"; } return ($@"# {meta.Title} {meta.MarkdownBody} <!-- Post Configuration --> <!-- ```xml <blogpost> <title>{meta.Title}</title> <abstract> {meta.Abstract} </abstract> <categories> {meta.Categories} </categories> <isDraft>{meta.IsDraft}</isDraft> <featuredImage>{meta.FeaturedImageUrl}</featuredImage> <keywords> {meta.Keywords} </keywords> <weblogs> <postid>{meta.PostId}</postid> <weblog> {meta.WeblogName} </weblog> </weblogs> </blogpost> ``` --> <!-- End Post Configuration --> "); }
/// <summary> /// This method sets the RawMarkdownBody /// </summary> /// <param name="meta"></param> /// <returns>Updated Markdown - also sets the RawMarkdownBody and MarkdownBody</returns> public string SetConfigInMarkdown(WeblogPostMetadata meta) { string markdown = meta.RawMarkdownBody; string origConfig = StringUtils.ExtractString(markdown, "<!-- Post Configuration -->", "<!-- End Post Configuration -->", false, false, true); string newConfig = $@"<!-- Post Configuration --> <!-- ```xml <blogpost> <title>{meta.Title}</title> <abstract> {meta.Abstract} </abstract> <categories> {meta.Categories} </categories> <keywords> {meta.Keywords} </keywords> <isDraft>{meta.IsDraft}</isDraft> <featuredImage>{meta.FeaturedImageUrl}</featuredImage> <weblogs> <postid>{meta.PostId}</postid> <weblog> {meta.WeblogName} </weblog> </weblogs> </blogpost> ``` --> <!-- End Post Configuration -->"; if (string.IsNullOrEmpty(origConfig)) { markdown += "\r\n" + newConfig; } else { markdown = markdown.Replace(origConfig, newConfig); } meta.RawMarkdownBody = markdown; meta.MarkdownBody = meta.RawMarkdownBody.Replace(newConfig, ""); return(markdown); }
public string NewWeblogPost(WeblogPostMetadata meta) { if (meta == null) { meta = new WeblogPostMetadata() { Title = "Post Title", MarkdownBody = string.Empty }; } if (string.IsNullOrEmpty(meta.WeblogName)) { meta.WeblogName = "Name of registered blog to post to"; } bool hasFrontMatter = meta.MarkdownBody != null && (meta.MarkdownBody.TrimStart().StartsWith("---\n") || meta.MarkdownBody.TrimStart().StartsWith("---\r")); string post; if (hasFrontMatter) { post = meta.MarkdownBody; } else { post = $@"# {meta.Title} {meta.MarkdownBody} "; } meta.RawMarkdownBody = post; meta.MarkdownBody = post; if (!hasFrontMatter) { post = meta.SetPostYaml(); } return(post); }
public string NewWeblogPost(WeblogPostMetadata meta) { if (meta == null) { meta = new WeblogPostMetadata() { Title = "Post Title", }; } if (string.IsNullOrEmpty(meta.WeblogName)) { meta.WeblogName = "Name of registered blog to post to"; } string post = $@"# {meta.Title} {meta.MarkdownBody} "; if (WeblogAddinConfiguration.Current.AddFrontMatterToNewBlogPost) { post = String.Format(WeblogAddinConfiguration.Current.FrontMatterTemplate, meta.Title, DateTime.Now) + post; } else { meta.RawMarkdownBody = post; meta.MarkdownBody = post; // Add Yaml data to post post = meta.SetPostYaml(); } return(post); }
private void WebLogStart_Loaded(object sender, System.Windows.RoutedEventArgs e) { //Model.LoadWebLognames(); var editor = Model.AppModel.ActiveEditor; if (editor == null) { return; } var markdown = editor.GetMarkdown(); Model.ActivePostMetadata = WeblogPostMetadata.GetPostConfigFromMarkdown(markdown, Model.ActivePost, Model.ActiveWeblogInfo); var lastBlog = WeblogAddinConfiguration.Current.LastWeblogAccessed; if (string.IsNullOrEmpty(Model.ActivePostMetadata.WeblogName)) { Model.ActivePostMetadata.WeblogName = lastBlog; } }
public string NewWeblogPost(WeblogPostMetadata meta) { if (meta == null) { meta = new WeblogPostMetadata() { Title = "Post Title", }; } if (string.IsNullOrEmpty(meta.WeblogName)) { meta.WeblogName = "Name of registered blog to post to"; } return ($@"# {meta.Title} <!-- Post Configuration --> <!-- ```xml <abstract> </abstract> <categories> </categories> <keywords> </keywords> <weblog> {meta.WeblogName} </weblog> ``` --> <!-- End Post Configuration --> "); }
/// <summary> /// Serializes a meta weblog post to YAML including extra fields. /// </summary> /// <param name="meta">MetaWebLogPostData instance or null which uses this object</param> /// <param name="addFrontMatterDashes">if true adds the leading and trailing Frontmatter dashes to the YAML</param> /// <returns></returns> public string SerializeToYaml(WeblogPostMetadata meta = null, bool addFrontMatterDashes = false) { if (meta == null) { meta = this; } var serializer = new SerializerBuilder() .WithNamingConvention(CamelCaseNamingConvention.Instance) .Build(); // hide fields if none are set var customFields = CustomFields; if (CustomFields != null && CustomFields.Count < 1) { CustomFields = null; } if (string.IsNullOrEmpty(PostId)) { PostId = null; } string yaml = serializer.Serialize(this); if (meta.ExtraValues.Count > 0) { yaml = yaml.TrimEnd() + mmApp.NewLine + ExtraValuesToYaml(ExtraValues); } if (addFrontMatterDashes) { yaml = $"---{mmApp.NewLine}{yaml}---{mmApp.NewLine}"; } return(yaml); }
public void CreateDownloadedPostOnDisk(Post post, string weblogName) { string filename = mmFileUtils.SafeFilename(post.Title); var folder = Path.Combine(WeblogAddinConfiguration.Current.PostsFolder, "Downloaded", weblogName, filename); if (!Directory.Exists(folder)) { Directory.CreateDirectory(folder); } var outputFile = Path.Combine(folder, StringUtils.ToCamelCase(filename) + ".md"); bool isMarkdown = false; string body = post.Body; string featuredImage = null; if (post.CustomFields != null) { var cf = post.CustomFields.FirstOrDefault(custf => custf.Id == "mt_markdown"); if (cf != null) { body = cf.Value; isMarkdown = true; } cf = post.CustomFields.FirstOrDefault(custf => custf.Id == "wp_post_thumbnail"); if (cf != null) { featuredImage = cf.Value; } } if (!isMarkdown) { if (!string.IsNullOrEmpty(post.mt_text_more)) { // Wordpress ReadMore syntax - SERIOUSLY??? if (string.IsNullOrEmpty(post.mt_excerpt)) { post.mt_excerpt = HtmlUtils.StripHtml(post.Body); } body = MarkdownUtilities.HtmlToMarkdown(body) + "\n\n<!--more-->\n\n" + MarkdownUtilities.HtmlToMarkdown(post.mt_text_more); } else { body = MarkdownUtilities.HtmlToMarkdown(body); } } string categories = null; if (post.Categories != null && post.Categories.Length > 0) { categories = string.Join(",", post.Categories); } // Create the new post by creating a file with title preset var meta = new WeblogPostMetadata() { Title = post.Title, MarkdownBody = body, Categories = categories, Keywords = post.mt_keywords, Abstract = post.mt_excerpt, PostId = post.PostId.ToString(), WeblogName = weblogName, FeaturedImageUrl = featuredImage }; string newPostMarkdown = NewWeblogPost(meta); File.WriteAllText(outputFile, newPostMarkdown); mmApp.Configuration.LastFolder = Path.GetDirectoryName(outputFile); if (isMarkdown) { string html = post.Body; string path = mmApp.Configuration.LastFolder; // do this synchronously so images show up :-< ShowStatus("Downloading post images..."); SaveMarkdownImages(html, path); ShowStatus("Post download complete.", 5000); //new Action<string,string>(SaveImages).BeginInvoke(html,path,null, null); } Model.Window.OpenTab(outputFile); }
/// <summary> /// Strips the Markdown Meta data from the message and populates /// the post structure with the meta data values. /// </summary> /// <param name="markdown">The raw markdown document with YAML header (optional)</param> /// <param name="post">Optional empty <seealso cref="Post"/> object that is filled with the meta data in.</param> public static WeblogPostMetadata GetPostYamlConfigFromMarkdown(string markdown, Post post = null) { var meta = new WeblogPostMetadata() { RawMarkdownBody = markdown, MarkdownBody = markdown, WeblogName = WeblogAddinConfiguration.Current.LastWeblogAccessed, CustomFields = new Dictionary <string, CustomField>() }; if (string.IsNullOrEmpty(markdown)) { return(meta); } markdown = markdown.Trim(); if (!markdown.StartsWith("---\n") && !markdown.StartsWith("---\r")) { return(meta); } // YAML with --- so we can replace string extractedYaml = MarkdownUtilities.ExtractFrontMatter(markdown, false); if (string.IsNullOrEmpty(extractedYaml)) { return(meta); } // just the YAML text var yaml = extractedYaml.Trim('-', ' ', '\r', '\n'); var input = new StringReader(yaml); var deserializer = new DeserializerBuilder() .IgnoreUnmatchedProperties() .WithNamingConvention(new CamelCaseNamingConvention()) .Build(); WeblogPostMetadata yamlMeta = null; try { yamlMeta = deserializer.Deserialize <WeblogPostMetadata>(input); } catch { return(meta); } if (yamlMeta == null) { return(meta); } if (meta.CustomFields == null) { meta.CustomFields = new Dictionary <string, CustomField>(); } meta = yamlMeta; meta.MarkdownBody = markdown.Replace(extractedYaml, ""); meta.RawMarkdownBody = markdown; meta.YamlFrontMatter = yaml; if (string.IsNullOrEmpty(meta.WeblogName)) { meta.WeblogName = WeblogAddinConfiguration.Current.LastWeblogAccessed; } if (post != null) { post.Title = meta.Title?.Trim(); if (!string.IsNullOrEmpty(meta.Categories)) { post.Categories = meta.Categories.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < post.Categories.Length; i++) { post.Categories[i] = post.Categories[i].Trim(); } } if (!string.IsNullOrEmpty(meta.Keywords)) { post.Tags = meta.Keywords.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); for (int i = 0; i < post.Tags.Length; i++) { post.Tags[i] = post.Tags[i].Trim(); } } post.Permalink = meta.Permalink; post.DateCreated = meta.PostDate; if (post.DateCreated < new DateTime(2000, 1, 1)) { post.DateCreated = DateTime.Now; } post.mt_excerpt = meta.Abstract; post.mt_keywords = meta.Keywords; if (meta.CustomFields != null) { post.CustomFields = meta.CustomFields.Values.ToArray(); } } return(meta); }
/// <summary> /// Strips the Markdown Meta data from the message and populates /// the post structure with the meta data values. /// </summary> /// <param name="markdown"></param> /// <returns></returns> public WeblogPostMetadata GetPostConfigFromMarkdown(string markdown) { var meta = new WeblogPostMetadata() { RawMarkdownBody = markdown, MarkdownBody = markdown, WeblogName = WeblogAddinConfiguration.Current.LastWeblogAccessed }; // check for title in first line and remove it // since the body shouldn't render the title var lines = StringUtils.GetLines(markdown); if (lines.Length > 0 && lines[0].StartsWith("# ")) { meta.MarkdownBody = meta.MarkdownBody.Replace(lines[0], "").Trim(); meta.Title = lines[0].Trim().Substring(2); } else if (lines.Length > 2 && lines[0] == "---" && meta.MarkdownBody.Contains("layout: post")) { var block = mmFileUtils.ExtractString(meta.MarkdownBody, "---", "---", returnDelimiters: true); if (!string.IsNullOrEmpty(block)) { meta.Title = StringUtils.ExtractString(block, "title: ", "\n").Trim(); meta.MarkdownBody = meta.MarkdownBody.Replace(block, "").Trim(); } } string config = StringUtils.ExtractString(markdown, "<!-- Post Configuration -->", "<!-- End Post Configuration -->", caseSensitive: false, allowMissingEndDelimiter: true, returnDelimiters: true); if (string.IsNullOrEmpty(config)) { return(meta); } // strip the config section meta.MarkdownBody = meta.MarkdownBody.Replace(config, ""); string title = StringUtils.ExtractString(config, "\n<title>", "</title>").Trim(); if (string.IsNullOrEmpty(meta.Title)) { meta.Title = title; } meta.Abstract = StringUtils.ExtractString(config, "\n<abstract>", "\n</abstract>").Trim(); meta.Keywords = StringUtils.ExtractString(config, "\n<keywords>", "\n</keywords>").Trim(); meta.Categories = StringUtils.ExtractString(config, "\n<categories>", "\n</categories>").Trim(); meta.PostId = StringUtils.ExtractString(config, "\n<postid>", "</postid>").Trim(); string strIsDraft = StringUtils.ExtractString(config, "\n<isDraft>", "</isDraft>").Trim(); if (strIsDraft != null && strIsDraft == "True") { meta.IsDraft = true; } string weblogName = StringUtils.ExtractString(config, "\n<weblog>", "</weblog>").Trim(); if (!string.IsNullOrEmpty(weblogName)) { meta.WeblogName = weblogName; } string featuredImageUrl = StringUtils.ExtractString(config, "\n<featuredImage>", "</featuredImage>"); if (!string.IsNullOrEmpty(featuredImageUrl)) { meta.FeaturedImageUrl = featuredImageUrl.Trim(); } ActivePost.Title = meta.Title; ActivePost.Categories = meta.Categories.Split(new [] { ',' }, StringSplitOptions.RemoveEmptyEntries); ActivePost.mt_excerpt = meta.Abstract; ActivePost.mt_keywords = meta.Keywords; return(meta); }
/// <summary> /// Parses a YAML block of text into a WeblogPostMetaData structure. /// Any fields that don't match the meta structure are parsed into /// ExtraFields /// </summary> /// <param name="yaml"></param> /// <returns></returns> public static WeblogPostMetadata ParseFromYaml(string yaml) { var parser = new YamlStream(); try { parser.Load(new StringReader(yaml)); } catch { return(null); } var meta = new WeblogPostMetadata(); var root = (YamlMappingNode)parser.Documents[0].RootNode; foreach (var entry in root.Children) { var key = entry.Key?.ToString(); if (string.IsNullOrEmpty(key)) { continue; } if (key.Equals("Title", StringComparison.OrdinalIgnoreCase)) { meta.Title = entry.Value?.ToString(); } else if (key.Equals("Abstract", StringComparison.OrdinalIgnoreCase)) { meta.Abstract = entry.Value?.ToString(); } else if (key.Equals("PostId", StringComparison.OrdinalIgnoreCase)) { meta.PostId = entry.Value?.ToString(); } else if (key.Equals("PostStatus", StringComparison.OrdinalIgnoreCase)) { meta.PostStatus = entry.Value?.ToString(); } else if (key.Equals("PostDate", StringComparison.OrdinalIgnoreCase)) { if (!DateTime.TryParse(entry.Value.ToString(), out DateTime dt)) { meta.PostDate = DateTime.UtcNow; } else { meta.PostDate = dt; } } else if (key.Equals("WeblogName", StringComparison.OrdinalIgnoreCase)) { meta.WeblogName = entry.Value?.ToString(); } else if (key.Equals("Keywords", StringComparison.OrdinalIgnoreCase)) { meta.Keywords = entry.Value?.ToString(); } else if (key.Equals("Categories", StringComparison.OrdinalIgnoreCase)) { meta.Categories = entry.Value?.ToString(); } else if (key.Equals("PermaLink", StringComparison.OrdinalIgnoreCase)) { meta.Permalink = entry.Value?.ToString(); } else if (key.Equals("FeaturedImageUrl", StringComparison.OrdinalIgnoreCase)) { meta.FeaturedImageUrl = entry.Value?.ToString(); } else if (key.Equals("FeaturedImageId", StringComparison.OrdinalIgnoreCase)) { meta.FeaturedImageId = entry.Value?.ToString(); } else if (key.Equals("DontInferFeaturedImage", StringComparison.OrdinalIgnoreCase)) { bool.TryParse(entry.Value.ToString(), out bool val); meta.DontInferFeaturedImage = val; } else if (key.Equals("DontStripH1Header", StringComparison.OrdinalIgnoreCase)) { bool.TryParse(entry.Value.ToString(), out bool val); meta.DontStripH1Header = val; } else if (key.Equals("CustomFields", StringComparison.OrdinalIgnoreCase)) { YamlMappingNode fields; try { fields = (YamlMappingNode)entry.Value; } catch { continue; // empty or missing } foreach (var item in fields.Children) { string k = item.Key.ToString(); string v = ((YamlMappingNode)item.Value).Children["value"].ToString(); meta.CustomFields.Add(k, new CustomField { Value = v, Key = k }); } } else { meta.ExtraValues.Add(entry.Key.ToString(), entry.Value?.ToString()); } } return(meta); }
/// <summary> /// Parses each of the images in the document and posts them to the server. /// Updates the HTML with the returned Image Urls /// </summary> /// <param name="html">HTML that contains images</param> /// <param name="filename">image file name</param> /// <param name="wrapper">blog wrapper instance that sends</param> /// <param name="metaData">metadata containing post info</param> /// <returns>update HTML string for the document with updated images</returns> private string SendImages(string html, string filename, MetaWeblogWrapper wrapper, WeblogPostMetadata metaData) { var basePath = Path.GetDirectoryName(filename); var baseName = Path.GetFileName(basePath); var doc = new HtmlDocument(); doc.LoadHtml(html); try { // send up normalized path images as separate media items var images = doc.DocumentNode.SelectNodes("//img"); if (images != null) { foreach (HtmlNode img in images) { string imgFile = img.Attributes["src"]?.Value as string; if (imgFile == null) { continue; } if (!imgFile.StartsWith("http://") && !imgFile.StartsWith("https://")) { imgFile = Path.Combine(basePath, imgFile.Replace("/", "\\")); if (File.Exists(imgFile)) { var media = new MediaObject() { Type = "application/image", Bits = File.ReadAllBytes(imgFile), Name = baseName + "/" + Path.GetFileName(imgFile) }; var mediaResult = wrapper.NewMediaObject(media); img.Attributes["src"].Value = mediaResult.URL; // use first image as featured image if (string.IsNullOrEmpty(metaData.FeaturedImageUrl)) { metaData.FeaturedImageUrl = mediaResult.URL; } } } } html = doc.DocumentNode.OuterHtml; } } catch (Exception ex) { MessageBox.Show("Error posting images to Weblog: " + ex.Message, mmApp.ApplicationName, MessageBoxButton.OK, MessageBoxImage.Exclamation); mmApp.Log(ex); return(null); } return(html); }
/// <summary> /// Strips the Markdown Meta data from the message and populates /// the post structure with the meta data values. /// </summary> /// <param name="markdown"></param> /// <param name="post"></param> /// <param name="weblogInfo"></param> public static WeblogPostMetadata GetPostYamlConfigFromMarkdown(string markdown, Post post, WeblogInfo weblogInfo) { var meta = new WeblogPostMetadata() { RawMarkdownBody = markdown, MarkdownBody = markdown, WeblogName = WeblogAddinConfiguration.Current.LastWeblogAccessed, CustomFields = new Dictionary <string, CustomField>() }; if (string.IsNullOrEmpty(markdown)) { return(meta); } markdown = markdown.Trim(); if (!markdown.StartsWith("---\n") && !markdown.StartsWith("---\r")) { return(meta); } string extractedYaml = null; //var match = YamlExtractionRegex.Match(markdown); var match = YamlExtractionRegex.Match(markdown); if (match.Success) { extractedYaml = match.Value; } //var extractedYaml = StringUtils.ExtractString(markdown.TrimStart(), "---\n", "\n---\n",returnDelimiters: true); if (string.IsNullOrEmpty(extractedYaml)) { return(meta); } var yaml = StringUtils.ExtractString(markdown, "---", "\n---", returnDelimiters: false).Trim(); var input = new StringReader(yaml); var deserializer = new DeserializerBuilder() .IgnoreUnmatchedProperties() .WithNamingConvention(new CamelCaseNamingConvention()) .Build(); WeblogPostMetadata yamlMeta = null; try { yamlMeta = deserializer.Deserialize <WeblogPostMetadata>(input); } catch { return(meta); } if (yamlMeta == null) { return(meta); } if (meta.CustomFields == null) { meta.CustomFields = new Dictionary <string, CustomField>(); } meta = yamlMeta; meta.MarkdownBody = markdown.Replace(extractedYaml, ""); meta.RawMarkdownBody = markdown; post.Title = meta.Title; if (!string.IsNullOrEmpty(meta.Categories)) { post.Categories = meta.Categories.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); } if (!string.IsNullOrEmpty(meta.Keywords)) { post.Tags = meta.Keywords.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); } post.mt_excerpt = meta.Abstract; post.mt_keywords = meta.Keywords; if (meta.CustomFields != null) { post.CustomFields = meta.CustomFields.Values.ToArray(); } return(meta); }
/// <summary> /// High level method that sends posts to the Weblog /// /// </summary> /// <returns></returns> public bool SendPost(WeblogInfo weblogInfo, bool sendAsDraft = false) { var editor = Model.ActiveEditor; if (editor == null) { return(false); } var doc = editor.MarkdownDocument; WeblogModel.ActivePost = new Post() { DateCreated = DateTime.Now }; // start by retrieving the current Markdown from the editor string markdown = editor.GetMarkdown(); // Retrieve Meta data from post and clean up the raw markdown // so we render without the config data var meta = WeblogPostMetadata.GetPostConfigFromMarkdown(markdown, WeblogModel.ActivePost, weblogInfo); string html = doc.RenderHtml(meta.MarkdownBody, WeblogAddinConfiguration.Current.RenderLinksOpenExternal); WeblogModel.ActivePost.Body = html; WeblogModel.ActivePost.PostId = meta.PostId; WeblogModel.ActivePost.PostStatus = meta.PostStatus; // Custom Field Processing: // Add custom fields from existing post // then add or update our custom fields var customFields = new Dictionary <string, CustomField>(); // load existing custom fields from post online if possible if (!string.IsNullOrEmpty(meta.PostId)) { var existingPost = GetPost(meta.PostId, weblogInfo); if (existingPost != null && meta.CustomFields != null && existingPost.CustomFields != null) { customFields = existingPost.CustomFields .ToDictionary(cf => cf.Key, cf => cf); } } // add custom fields from Weblog configuration if (weblogInfo.CustomFields != null) { foreach (var kvp in weblogInfo.CustomFields) { if (!customFields.ContainsKey(kvp.Key)) { AddOrUpdateCustomField(customFields, kvp.Key, kvp.Value); } } } // add custom fields from Meta data if (meta.CustomFields != null) { foreach (var kvp in meta.CustomFields) { AddOrUpdateCustomField(customFields, kvp.Key, kvp.Value.Value); } } if (!string.IsNullOrEmpty(markdown)) { AddOrUpdateCustomField(customFields, "mt_markdown", markdown); } WeblogModel.ActivePost.CustomFields = customFields.Values.ToArray(); var config = WeblogAddinConfiguration.Current; var kv = config.Weblogs.FirstOrDefault(kvl => kvl.Value.Name == meta.WeblogName); if (kv.Equals(default(KeyValuePair <string, WeblogInfo>))) { MessageBox.Show("Invalid Weblog configuration selected.", "Weblog Posting Failed", MessageBoxButton.OK, MessageBoxImage.Exclamation); return(false); } weblogInfo = kv.Value; var type = weblogInfo.Type; if (type == WeblogTypes.Unknown) { type = weblogInfo.Type; } string basePath = Path.GetDirectoryName(doc.Filename); string postUrl = null; if (type == WeblogTypes.MetaWeblogApi || type == WeblogTypes.Wordpress) { MetaWebLogWordpressApiClient client; client = new MetaWebLogWordpressApiClient(weblogInfo); // if values are already configured don't overwrite them again client.DontInferFeaturedImage = meta.DontInferFeaturedImage; client.FeaturedImageUrl = meta.FeaturedImageUrl; client.FeatureImageId = meta.FeaturedImageId; if (!client.PublishCompletePost(WeblogModel.ActivePost, basePath, sendAsDraft, markdown)) { mmApp.Log($"Error sending post to Weblog at {weblogInfo.ApiUrl}: " + client.ErrorMessage); MessageBox.Show("Error sending post to Weblog: " + client.ErrorMessage, mmApp.ApplicationName, MessageBoxButton.OK, MessageBoxImage.Exclamation); return(false); } var post = client.GetPost(WeblogModel.ActivePost.PostId); if (post != null) { postUrl = post.Url; } } if (type == WeblogTypes.Medium) { var client = new MediumApiClient(weblogInfo); var result = client.PublishCompletePost(WeblogModel.ActivePost, basePath, sendAsDraft); if (result == null) { mmApp.Log($"Error sending post to Weblog at {weblogInfo.ApiUrl}: " + client.ErrorMessage); MessageBox.Show($"Error sending post to Weblog: " + client.ErrorMessage, mmApp.ApplicationName, MessageBoxButton.OK, MessageBoxImage.Exclamation); return(false); } // this is null postUrl = client.PostUrl; } meta.PostId = WeblogModel.ActivePost.PostId.ToString(); // retrieve the raw editor markdown markdown = editor.GetMarkdown(); meta.RawMarkdownBody = markdown; // add the meta configuration to it markdown = meta.SetPostYaml(); // write it back out to editor editor.SetMarkdown(markdown, updateDirtyFlag: true); try { // preview post if (!string.IsNullOrEmpty(weblogInfo.PreviewUrl)) { var url = weblogInfo.PreviewUrl.Replace("{0}", WeblogModel.ActivePost.PostId.ToString()); ShellUtils.GoUrl(url); } else { if (!string.IsNullOrEmpty(postUrl)) { ShellUtils.GoUrl(postUrl); } else { ShellUtils.GoUrl(new Uri(weblogInfo.ApiUrl).GetLeftPart(UriPartial.Authority)); } } } catch { mmApp.Log("Failed to display Weblog Url after posting: " + weblogInfo.PreviewUrl ?? postUrl ?? weblogInfo.ApiUrl); } return(true); }
/// <summary> /// Strips the Markdown Meta data from the message and populates /// the post structure with the meta data values. /// </summary> /// <param name="markdown"></param> /// <param name="post"></param> /// <param name="weblogInfo"></param> /// <returns></returns> public static WeblogPostMetadata GetPostConfigFromMarkdown(string markdown, Post post, WeblogInfo weblogInfo) { var meta = new WeblogPostMetadata() { RawMarkdownBody = markdown, MarkdownBody = markdown, WeblogName = WeblogAddinConfiguration.Current.LastWeblogAccessed, CustomFields = new Dictionary <string, CustomField>() }; // check for title in first line and remove it // since the body shouldn't render the title var lines = StringUtils.GetLines(markdown, 40); if (lines.Length > 2 && lines[0] == "---") { var block = mmFileUtils.ExtractString(meta.MarkdownBody, "---", "\n---", returnDelimiters: true); if (!string.IsNullOrEmpty(block)) { meta = WeblogPostMetadata.GetPostYamlConfigFromMarkdown(markdown, post, weblogInfo); meta.RawMarkdownBody = markdown; if (string.IsNullOrEmpty(meta.WeblogName)) { meta.WeblogName = WeblogAddinConfiguration.Current.LastWeblogAccessed; } meta.MarkdownBody = meta.MarkdownBody.Replace(block, "").Trim(); // update to what's left lines = StringUtils.GetLines(meta.MarkdownBody, 10); } } if (lines.Length > 0 && lines[0].StartsWith("# ")) { if (!meta.DontStripH1Header && weblogInfo.Type != WeblogTypes.Medium) // medium wants the header in the text { meta.MarkdownBody = meta.MarkdownBody.Replace(lines[0], "").Trim(); } if (string.IsNullOrEmpty(meta.Title)) { meta.Title = lines[0].Trim().Substring(2); } } string config = StringUtils.ExtractString(markdown, "<!-- Post Configuration -->", "<!-- End Post Configuration -->", caseSensitive: false, allowMissingEndDelimiter: true, returnDelimiters: true); if (string.IsNullOrEmpty(config)) { return(meta); } // strip the config section meta.MarkdownBody = meta.MarkdownBody.Replace(config, ""); string title = StringUtils.ExtractString(config, "\n<title>", "</title>").Trim(); if (string.IsNullOrEmpty(meta.Title)) { meta.Title = title; } meta.Abstract = StringUtils.ExtractString(config, "\n<abstract>", "\n</abstract>").Trim(); meta.Keywords = StringUtils.ExtractString(config, "\n<keywords>", "\n</keywords>").Trim(); meta.Categories = StringUtils.ExtractString(config, "\n<categories>", "\n</categories>").Trim(); meta.PostId = StringUtils.ExtractString(config, "\n<postid>", "</postid>").Trim(); string strIsDraft = StringUtils.ExtractString(config, "\n<isDraft>", "</isDraft>").Trim(); if (strIsDraft != null && strIsDraft == "True") { meta.PostStatus = "draft"; } string weblogName = StringUtils.ExtractString(config, "\n<weblog>", "</weblog>").Trim(); if (!string.IsNullOrEmpty(weblogName)) { meta.WeblogName = weblogName; } string inferFeaturedImage = StringUtils.ExtractString(config, "\n<inferFeaturedImage>", "</inferFeaturedImage>"); if (!string.IsNullOrEmpty(inferFeaturedImage)) { meta.DontInferFeaturedImage = inferFeaturedImage != "False" && inferFeaturedImage != "false"; } string featuredImageUrl = StringUtils.ExtractString(config, "\n<featuredImage>", "</featuredImage>"); if (!string.IsNullOrEmpty(featuredImageUrl)) { meta.FeaturedImageUrl = featuredImageUrl.Trim(); } string featuredImageId = StringUtils.ExtractString(config, "\n<featuredImageId>", "</featuredImageId>"); if (!string.IsNullOrEmpty(featuredImageId)) { meta.FeaturedImageId = featuredImageId.Trim(); } string customFieldsString = StringUtils.ExtractString(config, "\n<customFields>", "</customFields>", returnDelimiters: true); if (!string.IsNullOrEmpty(customFieldsString)) { try { var dom = new XmlDocument(); dom.LoadXml(customFieldsString); foreach (XmlNode child in dom.DocumentElement.ChildNodes) { if (child.NodeType == XmlNodeType.Element) { var key = child.FirstChild.InnerText; var value = child.ChildNodes[1].InnerText; string id = null; if (child.ChildNodes.Count > 2) { id = child.ChildNodes[2].InnerText; } meta.CustomFields.Add(key, new CustomField { Key = key, Value = value, Id = id }); } } } catch { } } post.Title = meta.Title; post.Categories = meta.Categories.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); post.Tags = meta.Keywords.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); post.mt_excerpt = meta.Abstract; post.mt_keywords = meta.Keywords; return(meta); }
/// <summary> /// This method sets the RawMarkdownBody /// </summary> /// <param name="meta"></param> /// <returns>Updated Markdown - also sets the RawMarkdownBody and MarkdownBody</returns> public string SetConfigInMarkdown(WeblogPostMetadata meta) { string markdown = meta.RawMarkdownBody; string customFields = null; if (meta.CustomFields != null && meta.CustomFields.Count > 0) { StringBuilder sb = new StringBuilder(); sb.AppendLine(); sb.AppendLine("<customFields>"); foreach (var cf in meta.CustomFields) { sb.AppendLine("\t<customField>"); sb.AppendLine($"\t\t<key>{cf.Key}</key>"); sb.AppendLine($"\t\t<value>{System.Net.WebUtility.HtmlEncode(cf.Value)}</value>"); sb.AppendLine("\t</customField>"); } sb.AppendLine("</customFields>"); customFields = sb.ToString(); } string origConfig = StringUtils.ExtractString(markdown, "<!-- Post Configuration -->", "<!-- End Post Configuration -->", false, false, true); string newConfig = $@"<!-- Post Configuration --> <!-- ```xml <blogpost> <title>{meta.Title}</title> <abstract> {meta.Abstract} </abstract> <categories> {meta.Categories} </categories> <keywords> {meta.Keywords} </keywords> <isDraft>{meta.IsDraft}</isDraft> <featuredImage>{meta.FeaturedImageUrl}</featuredImage>{customFields} <weblogs> <postid>{meta.PostId}</postid> <weblog> {meta.WeblogName} </weblog> </weblogs> </blogpost> ``` --> <!-- End Post Configuration -->"; if (string.IsNullOrEmpty(origConfig)) { markdown += "\r\n" + newConfig; } else { markdown = markdown.Replace(origConfig, newConfig); } meta.RawMarkdownBody = markdown; meta.MarkdownBody = meta.RawMarkdownBody.Replace(newConfig, ""); return(markdown); }