logger = null
)
{
}
private const int defaultLengthWords = 20;
private const int defaultLengthCharacters = 200;
private const int defaultLengthAbsolute = 30;
private const string terminator = "";
///
/// this is an expensive method as often many retries ar eneeded to produce a valid html fragment.
/// therefore this should not be used for dynamic excerpt generation.
/// Excerpt should be generated and saved when content is edited.
///
///
///
///
///
///
public ExcerptResult GenerateExcerpt(
ExcerptTruncationMode truncationMode,
int truncationLength,
string html,
//string cacheKey,
//string slug,
string languageCode //,
//bool logWarnings = true
)
{
var result = new ExcerptResult();
if (string.IsNullOrWhiteSpace(html))
{
result.HtmlContent = html;
result.DidTruncate = false;
return result;
}
// Try to get language metadata
var cultureInfo = CultureInfo.InvariantCulture;
if (!string.IsNullOrEmpty(languageCode))
{
try
{
cultureInfo = new CultureInfo(languageCode);
}
catch (CultureNotFoundException) { }
}
var contentLength = GetContentLength(html, truncationMode);
if (contentLength <= truncationLength)
{
result.HtmlContent = html;
result.DidTruncate = false;
return result;
}
//if (_cache != null)
//{
// var cachedTeaser = _cache.GetTeaser(cacheKey);
// if (!string.IsNullOrEmpty(cachedTeaser))
// {
// result.Content = cachedTeaser;
// result.DidTruncate = true;
// return result;
// }
//}
var isRightToLeftLanguage = cultureInfo.TextInfo.IsRightToLeft;
// Get global teaser settings.
var truncationLengthToUse = truncationLength <= 0 ? GetDefaultTeaserLength(truncationMode) : truncationLength;
// Truncate the raw content first. In general, Humanizer is smart enough to ignore tags, especially if using word truncation.
var text = TruncatePost(truncationMode, html, truncationLengthToUse, isRightToLeftLanguage);
// Don't leave dangling tags.
HtmlNode.ElementsFlags["p"] = HtmlElementFlag.Closed;
//var modeDesc = GetModeDescription(truncationMode);
//if we get bad output try increasing the allowed length unti it is valid
while (!IsValidMarkup(text) && truncationLengthToUse <= contentLength)
{
truncationLengthToUse += 1;
//if (_log != null && logWarnings)
//{
// _log.LogWarning($"teaser truncation for post {slug}, produced invalid html, so trying again and increasing the truncation length to {truncationLengthToUse} {modeDesc}. You should re-publish this post to create a persistent teaser.");
//}
text = TruncatePost(truncationMode, html, truncationLengthToUse, isRightToLeftLanguage);
}
if (!IsValidMarkup(text))
{
//if (_log != null)
//{
// _log.LogError($"failed to create valid teaser for post {slug}, so returning full content");
//}
result.HtmlContent = html;
result.DidTruncate = false;
return result;
}
//if (_cache != null)
//{
// _cache.AddToCache(text, cacheKey);
//}
var doc = new HtmlDocument();
doc.LoadHtml(text);
result.HtmlContent = doc.DocumentNode.InnerHtml;
result.DidTruncate = true;
return result;
}
private bool IsValidMarkup(string html)
{
var errors = GetMarkupErrors(html);
return errors.Count() == 0;
}
private IEnumerable GetMarkupErrors(string html)
{
var document = new HtmlAgilityPack.HtmlDocument();
document.OptionFixNestedTags = true;
document.LoadHtml(html);
return document.ParseErrors;
}
//private string GetModeDescription(ExcerptTruncationMode mode)
//{
// switch (mode)
// {
// case ExcerptTruncationMode.Length:
// return "string length";
// case ExcerptTruncationMode.Character:
// return "letters or digits";
// case ExcerptTruncationMode.Word:
// default:
// return "words";
// }
//}
private int GetContentLength(string html, ExcerptTruncationMode mode)
{
if (string.IsNullOrEmpty(html)) return 0;
switch (mode)
{
case ExcerptTruncationMode.Length:
return html.Length;
case ExcerptTruncationMode.Character:
return html.ToCharArray().Count(char.IsLetterOrDigit);
case ExcerptTruncationMode.Word:
default:
return html.Split((char[])null, StringSplitOptions.RemoveEmptyEntries).Count();
}
}
// Internal for unit testing purposes only.
private int GetDefaultTeaserLength(ExcerptTruncationMode mode)
{
switch (mode)
{
case ExcerptTruncationMode.Length:
return defaultLengthAbsolute;
case ExcerptTruncationMode.Character:
return defaultLengthCharacters;
case ExcerptTruncationMode.Word:
default:
return defaultLengthWords;
}
}
// Internal for unit testing purposes only.
private string TruncatePost(ExcerptTruncationMode mode, string content, int length, bool isRightToLeftLanguage = false)
{
var truncateFrom = isRightToLeftLanguage ? TruncateFrom.Left : TruncateFrom.Right;
switch (mode)
{
case ExcerptTruncationMode.Length:
return content.Truncate(length, terminator, Truncator.FixedLength, truncateFrom);
case ExcerptTruncationMode.Character:
return content.Truncate(length, terminator, Truncator.FixedNumberOfCharacters, truncateFrom);
case ExcerptTruncationMode.Word:
default:
return content.Truncate(length, terminator, Truncator.FixedNumberOfWords, truncateFrom);
}
}
}
}
================================================
FILE: src/cloudscribe.ContentUtils/ExcerptResult.cs
================================================
namespace cloudscribe.ContentUtils
{
public class ExcerptResult
{
public string HtmlContent { get; set; }
public bool DidTruncate { get; set; }
}
}
================================================
FILE: src/cloudscribe.ContentUtils/MarkdownHelper.cs
================================================
using Markdig;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
using System;
using System.Linq;
namespace cloudscribe.ContentUtils
{
public class MarkdownHelper
{
private MarkdownPipeline _mdPipeline = null;
public string ConvertMarkdownToHtml(string markdown)
{
if (_mdPipeline == null)
{
_mdPipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
}
return Markdown.ToHtml(markdown, _mdPipeline);
}
public string ExtractFirstImageUrl(string markdown)
{
if (_mdPipeline == null)
{
_mdPipeline = new MarkdownPipelineBuilder().UseAdvancedExtensions().Build();
}
if (!String.IsNullOrWhiteSpace(markdown))
{
var doc = Markdown.Parse(markdown, _mdPipeline);
var img = doc.Descendants()
.SelectMany(x => x.Inline.Descendants())
.FirstOrDefault(l => l.IsImage);
if (img != null)
{
return img.Url;
}
}
return string.Empty;
}
}
}
================================================
FILE: src/cloudscribe.ContentUtils/README.md
================================================
# cloudscribe.ContentUtils
[](https://www.nuget.org/packages/cloudscribe.ContentUtils)
[](https://opensource.org/licenses/Apache-2.0)
Utilities for content management and manipulation in cloudscribe projects.
## Installation
```shell
Install-Package cloudscribe.ContentUtils
```
## Usage
Add as a dependency to your .NET 8.0 project and use the provided utilities for content handling.
## Contributing
Contributions are welcome! Please see the [contributing guidelines](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/CONTRIBUTING.md).
## License
This project is licensed under the Apache 2.0 License - see the [LICENSE](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/LICENSE) file for details.
================================================
FILE: src/cloudscribe.ContentUtils/SlugHelper.cs
================================================
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace cloudscribe.ContentUtils
{
public static class SlugHelper
{
public static string CreateSlug(string title)
{
if (string.IsNullOrWhiteSpace(title)) { return title; }
title = title.ToLowerInvariant().Replace(" ", " ")
.Replace(" ", "-")
.Replace("--", "-")
.Replace("--", "-")
.Replace("\n", string.Empty)
.Replace("\r", string.Empty)
.Replace("\t", string.Empty)
;
title = RemoveDiacritics(title);
title = RemoveReservedUrlCharacters(title);
return title.ToLowerInvariant().Trim();
}
private static string RemoveDiacritics(string text)
{
if (string.IsNullOrWhiteSpace(text)) { return text; }
var normalizedString = text.Normalize(NormalizationForm.FormD);
var stringBuilder = new StringBuilder();
foreach (var c in normalizedString)
{
var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
if (unicodeCategory != UnicodeCategory.NonSpacingMark)
{
stringBuilder.Append(c);
}
}
return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
}
private static string RemoveReservedUrlCharacters(string text)
{
if (string.IsNullOrWhiteSpace(text)) { return text; }
var reservedCharacters = new List() { "!", "#", "$", "&", "'", "(", ")", "*", ",", "/", ":", ";", "=", "?", "@", "[", "]", "\"", "%", ".", "<", ">", "\\", "^", "_", "'", "{", "}", "|", "~", "`", "+" };
foreach (var chr in reservedCharacters)
{
text = text.Replace(chr, "");
}
return text;
}
}
}
================================================
FILE: src/cloudscribe.ContentUtils/Truncation.cs
================================================
using System;
using System.Linq;
namespace cloudscribe.ContentUtils
{
public static class Truncation
{
///
/// Truncate the string
///
/// The string to be truncated
/// The length to truncate to
/// The truncated string
public static string Truncate(this string input, int length)
{
return input.Truncate(length, "…", Truncator.FixedLength);
}
///
/// Truncate the string
///
/// The string to be truncated
/// The length to truncate to
/// The truncate to use
/// The enum value used to determine from where to truncate the string
/// The truncated string
public static string Truncate(this string input, int length, ITruncator truncator, TruncateFrom from = TruncateFrom.Right)
{
return input.Truncate(length, "…", truncator, from);
}
///
/// Truncate the string
///
/// The string to be truncated
/// The length to truncate to
/// The string used to truncate with
/// The enum value used to determine from where to truncate the string
/// The truncated string
public static string Truncate(this string input, int length, string truncationString, TruncateFrom from = TruncateFrom.Right)
{
return input.Truncate(length, truncationString, Truncator.FixedLength, from);
}
///
/// Truncate the string
///
/// The string to be truncated
/// The length to truncate to
/// The string used to truncate with
/// The truncator to use
/// The enum value used to determine from where to truncate the string
/// The truncated string
public static string Truncate(this string input, int length, string truncationString, ITruncator truncator, TruncateFrom from = TruncateFrom.Right)
{
if (truncator == null)
throw new ArgumentNullException(nameof(truncator));
if (input == null)
return null;
return truncator.Truncate(input, length, truncationString, from);
}
}
///
/// Truncation location for humanizer
///
public enum TruncateFrom
{
///
/// Truncate letters from the left (start) of the string
///
Left,
///
/// Truncate letters from the right (end) of the string
///
Right
}
public enum ExcerptTruncationMode : byte
{
///
/// (Default) Truncate the post based on number of words.
///
Word = 0,
///
/// Truncate the post to a fixed length.
///
Length,
///
/// Truncate the post based on number of characters.
///
Character
}
public interface ITruncator
{
///
/// Truncate a string
///
/// The string to truncate
/// The length to truncate to
/// The string used to truncate with
/// The enum value used to determine from where to truncate the string
/// The truncated string
string Truncate(string value, int length, string truncationString, TruncateFrom truncateFrom = TruncateFrom.Right);
}
public static class Truncator
{
///
/// Fixed length truncator
///
public static ITruncator FixedLength
{
get
{
return new FixedLengthTruncator();
}
}
///
/// Fixed number of characters truncator
///
public static ITruncator FixedNumberOfCharacters
{
get
{
return new FixedNumberOfCharactersTruncator();
}
}
///
/// Fixed number of words truncator
///
public static ITruncator FixedNumberOfWords
{
get
{
return new FixedNumberOfWordsTruncator();
}
}
}
class FixedLengthTruncator : ITruncator
{
public string Truncate(string value, int length, string truncationString, TruncateFrom truncateFrom = TruncateFrom.Right)
{
if (value == null)
return null;
if (value.Length == 0)
return value;
if (truncationString == null || truncationString.Length > length)
return truncateFrom == TruncateFrom.Right
? value.Substring(0, length)
: value.Substring(value.Length - length);
if (truncateFrom == TruncateFrom.Left)
return value.Length > length
? truncationString + value.Substring(value.Length - length + truncationString.Length)
: value;
return value.Length > length
? value.Substring(0, length - truncationString.Length) + truncationString
: value;
}
}
class FixedNumberOfCharactersTruncator : ITruncator
{
public string Truncate(string value, int length, string truncationString, TruncateFrom truncateFrom = TruncateFrom.Right)
{
if (value == null)
return null;
if (value.Length == 0)
return value;
if (truncationString == null)
truncationString = string.Empty;
if (truncationString.Length > length)
return truncateFrom == TruncateFrom.Right ? value.Substring(0, length) : value.Substring(value.Length - length);
var alphaNumericalCharactersProcessed = 0;
if (value.ToCharArray().Count(char.IsLetterOrDigit) <= length)
return value;
if (truncateFrom == TruncateFrom.Left)
{
for (var i = value.Length - 1; i > 0; i--)
{
if (char.IsLetterOrDigit(value[i]))
alphaNumericalCharactersProcessed++;
if (alphaNumericalCharactersProcessed + truncationString.Length == length)
return truncationString + value.Substring(i);
}
}
for (var i = 0; i < value.Length - truncationString.Length; i++)
{
if (char.IsLetterOrDigit(value[i]))
alphaNumericalCharactersProcessed++;
if (alphaNumericalCharactersProcessed + truncationString.Length == length)
return value.Substring(0, i + 1) + truncationString;
}
return value;
}
}
class FixedNumberOfWordsTruncator : ITruncator
{
public string Truncate(string value, int length, string truncationString, TruncateFrom truncateFrom = TruncateFrom.Right)
{
if (value == null)
return null;
if (value.Length == 0)
return value;
var numberOfWords = value.Split((char[])null, StringSplitOptions.RemoveEmptyEntries).Count();
if (numberOfWords <= length)
return value;
return truncateFrom == TruncateFrom.Left
? TruncateFromLeft(value, length, truncationString)
: TruncateFromRight(value, length, truncationString);
}
private static string TruncateFromRight(string value, int length, string truncationString)
{
var lastCharactersWasWhiteSpace = true;
var numberOfWordsProcessed = 0;
for (var i = 0; i < value.Length; i++)
{
if (char.IsWhiteSpace(value[i]))
{
if (!lastCharactersWasWhiteSpace)
numberOfWordsProcessed++;
lastCharactersWasWhiteSpace = true;
if (numberOfWordsProcessed == length)
return value.Substring(0, i) + truncationString;
}
else
lastCharactersWasWhiteSpace = false;
}
return value + truncationString;
}
private static string TruncateFromLeft(string value, int length, string truncationString)
{
var lastCharactersWasWhiteSpace = true;
var numberOfWordsProcessed = 0;
for (var i = value.Length - 1; i > 0; i--)
{
if (char.IsWhiteSpace(value[i]))
{
if (!lastCharactersWasWhiteSpace)
numberOfWordsProcessed++;
lastCharactersWasWhiteSpace = true;
if (numberOfWordsProcessed == length)
return truncationString + value.Substring(i + 1).TrimEnd();
}
else
lastCharactersWasWhiteSpace = false;
}
return truncationString + value;
}
}
}
================================================
FILE: src/cloudscribe.ContentUtils/cloudscribe.ContentUtils.csproj
================================================
Content utilities for html and markdown
10.1.0
net10.0
Joe Audette
cloudscribe;blog,content
icon.png
https://github.com/cloudscribe/cloudscribe.SimpleContent
Apache-2.0
https://github.com/cloudscribe/cloudscribe.SimpleContent.git
git
README.md
================================================
FILE: src/cloudscribe.Core.SimpleContent/AuthorNameResolver.cs
================================================
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Models;
using cloudscribe.SimpleContent.Web;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Integration
{
public class AuthorNameResolver : IAuthorNameResolver
{
public AuthorNameResolver(IUserContextResolver userContextResolver)
{
userResolver = userContextResolver;
}
private IUserContextResolver userResolver;
public async Task GetAuthorName(ClaimsPrincipal user, CancellationToken cancellationToken = default(CancellationToken))
{
var dbUser = await userResolver.GetCurrentUser(cancellationToken);
string result;
if(dbUser == null)
{
result = user.GetUserDisplayName();
if (string.IsNullOrEmpty(result))
{
result = user.Identity.Name;
}
return result;
}
if(!string.IsNullOrWhiteSpace(dbUser.FirstName))
{
if(!string.IsNullOrWhiteSpace(dbUser.LastName))
{
return dbUser.FirstName + " " + dbUser.LastName;
}
}
return dbUser.DisplayName;
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/ClaimsPrincipalExtensions.cs
================================================
//// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
//// Author: Joe Audette
//// Created: 2016-02-09
//// Last Modified: 2016-08-12
////
//using System;
//using System.Security.Claims;
//using Microsoft.AspNetCore.Authorization;
//namespace cloudscribe.Core.SimpleContent.Integration
//{
// public static class ClaimsPrincipalExtensions
// {
// public static string GetEmail(this ClaimsPrincipal principal)
// {
// if (principal == null)
// {
// throw new ArgumentNullException(nameof(principal));
// }
// var claim = principal.FindFirst("Email");
// return claim != null ? claim.Value : null;
// }
// public static string GetUserDisplayName(this ClaimsPrincipal principal)
// {
// if (principal == null)
// {
// throw new ArgumentNullException(nameof(principal));
// }
// var claim = principal.FindFirst("DisplayName");
// return claim != null ? claim.Value : null;
// }
// public static string GetProjectId(this ClaimsPrincipal principal)
// {
// if (principal == null)
// {
// throw new ArgumentNullException(nameof(principal));
// }
// var claim = principal.FindFirst("ContentEditor");
// return claim != null ? claim.Value : null;
// }
// public static bool CanEditProject(this ClaimsPrincipal principal, string projectId)
// {
// if (principal == null)
// {
// throw new ArgumentNullException(nameof(principal));
// }
// var claim = principal.FindFirst("ContentEditor");
// if (claim == null) { return false; }
// if (claim.Value == projectId) { return true; }
// return false;
// }
// public static bool CanEditPages(
// this ClaimsPrincipal principal,
// string projectId,
// IAuthorizationService authorizationService = null
// )
// {
// if (principal == null)
// {
// throw new ArgumentNullException(nameof(principal));
// }
// if (principal.CanEditProject(projectId)) return true;
// var claim = principal.FindFirst("PageEditor");
// if (claim == null) { return false; }
// if (claim.Value == projectId) { return true; }
// return false;
// }
// public static bool CanEditBlog(this ClaimsPrincipal principal, string projectId)
// {
// if (principal == null)
// {
// throw new ArgumentNullException(nameof(principal));
// }
// if (principal.CanEditProject(projectId)) return true;
// var claim = principal.FindFirst("BlogEditor");
// if (claim == null) { return false; }
// if (claim.Value == projectId) { return true; }
// return false;
// }
// }
//}
================================================
FILE: src/cloudscribe.Core.SimpleContent/ContentSettingsUIConfig.cs
================================================
using System;
using System.Collections.Generic;
using System.Text;
namespace cloudscribe.Core.SimpleContent
{
public class ContentSettingsUIConfig
{
public bool ShowBlogMenuOptions { get; set; } = true;
public bool ShowBlogSettings { get; set; } = true;
public bool ShowPageSettings { get; set; } = true;
public bool ShowDefaultContentType { get; set; } = false;
public bool ShowCommentSettings { get; set; } = true;
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/Controllers/ContentCloningController.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-08-07
// Last Modified: 2019-03-04
//
using cloudscribe.Core.Models;
using cloudscribe.Core.SimpleContent.Integration.ViewModels;
using cloudscribe.Core.Web.Components;
using cloudscribe.SimpleContent.Models;
using cloudscribe.SimpleContent.Web.Services;
using cloudscribe.Web.Common.Extensions;
using cloudscribe.Web.Navigation;
using cloudscribe.Web.Navigation.Caching;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Integration.Mvc.Controllers
{
public class ContentCloningController : Controller
{
private readonly IProjectService _projectService;
private readonly SiteManager _siteManager;
private readonly ISiteQueries _siteQueries;
private readonly IProjectCommands _projectCommands;
private readonly IPageQueries _pageQueries;
private readonly IPageCommands _pageCommands;
private readonly IPostQueries _postQueries;
private readonly IPostCommands _postCommands;
private readonly IConfiguration _configuration;
private readonly ITreeCache _treeCache;
private readonly IStringLocalizer sr;
public ContentCloningController(
IProjectService projectService,
SiteManager siteManager,
ISiteQueries siteQueries,
IProjectCommands projectCommands,
IPageQueries pageQueries,
IPageCommands pageCommands,
IPostQueries postQueries,
IPostCommands postCommands,
IConfiguration configuration,
ITreeCache treeCache,
IStringLocalizer localizer
)
{
_projectService = projectService;
_siteManager = siteManager;
_siteQueries = siteQueries;
_projectCommands = projectCommands;
_pageQueries = pageQueries;
_pageCommands = pageCommands;
_postQueries = postQueries;
_postCommands = postCommands;
_configuration = configuration;
_treeCache = treeCache;
sr = localizer;
}
[Authorize(Policy = "AdminPolicy")]
// GET: /ContentCloning/index[?siteId=]
[HttpGet]
public async Task Index(string siteId = null)
{
ViewData["Title"] = sr["Content Cloning"];
var model = new ContentCloningViewModel() { SiteId = siteId };
model = await PopulateAndValidateModel(model); //add the list of sites to the model and do validation
return View(model);
}
[Authorize(Policy = "AdminPolicy")]
[HttpPost]
public async Task Index(ContentCloningViewModel model)
{
ViewData["Title"] = sr["Content Cloning"];
if (!ModelState.IsValid)
{
return View(model);
}
model = await PopulateAndValidateModel(model); //add the list of sites to the model and do validation
if (!model.CloneAllowed)
{
return View(model);
}
if(string.IsNullOrWhiteSpace(model.Command) || model.Command != "clone")
{
return View(model);
}
// at this point we're ready to clone the ProjectSettings, Pages and Posts
//Project aka Site Content Settings
if(model.CloneContentSettings)
{
try
{
string projectId = await _projectCommands.CloneToNewProject(
model.CloneFromSiteId,
model.CloneToSiteId,
model.CloneToSiteName
);
this.AlertSuccess(sr["Content Settings cloning was successful!"], true);
}
catch(Exception ex)
{
this.AlertDanger(sr["Failed to clone the Content Settings!"], true);
this.AlertDanger(ex.Message, true);
return View(model);
}
}
//Clone the Pages. This has to be done in two passes because we need to know the new page ids
//in order to properly update the parent child relationships
int pageCopyCount = 0;
int pageUpdateCount = 0;
if(model.ClonePages)
{
bool copySuccessful = false;
try
{
List pages = await _pageQueries.GetAllPages(model.CloneFromSiteId);
foreach(var page in pages)
{
string pageId = await _pageCommands.CloneToNewProject(
model.CloneFromSiteId,
model.CloneToSiteId,
page.Id
);
if(!string.IsNullOrWhiteSpace(pageId)) pageCopyCount++;
}
copySuccessful = true;
}
catch (Exception ex)
{
this.AlertDanger(string.Format(sr["An error occurred while cloning content pages. Only {0}/{1} were copied."], pageCopyCount, model.CloneFromPageCount), true);
this.AlertDanger(ex.Message, true);
}
if(copySuccessful)
{
try
{
List newPages = await _pageQueries.GetAllPages(model.CloneToSiteId);
List oldPages = await _pageQueries.GetAllPages(model.CloneFromSiteId);
foreach (var newPage in newPages)
{
if(newPage.ParentId != null && newPage.ParentId != "0")
{
// find the old parent of the page that has been copied
var oldParent = oldPages.FirstOrDefault(x => x.Id == newPage.ParentId);
if (oldParent != null)
{
// find the matching new page that will now be the new parent
var newParent = newPages.FirstOrDefault(x => x.Slug == oldParent.Slug);
if (newParent != null)
{
newPage.ParentId = newParent.Id;
newPage.ParentSlug = newParent.Slug;
}
else //orphaned page - can't find the old parent's slug among the newly cloned pages
{
newPage.ParentId = "0";
newPage.ParentSlug = "";
}
}
else //orphaned page - can't find the old parent Id of the page that has been copied
{
newPage.ParentId = "0";
newPage.ParentSlug = "";
}
await _pageCommands.Update(model.CloneToSiteId, newPage);
}
pageUpdateCount++;
}
}
catch (Exception ex)
{
this.AlertDanger(string.Format(sr["An error occurred while updating content pages tree. Only {0}/{1} were updated."], pageUpdateCount, model.CloneFromPageCount), true);
this.AlertDanger(ex.Message, true);
}
}
if(pageCopyCount == model.CloneFromPageCount && pageUpdateCount == model.CloneFromPageCount)
{
this.AlertSuccess(sr["Content pages cloning was successful!"], true);
}
}
//Clone the Posts
int postCount = 0;
if (model.CloneBlogPosts)
{
try
{
List posts = await _postQueries.GetPosts(model.CloneFromSiteId, true);
foreach (var post in posts)
{
string postId = await _postCommands.CloneToNewProject(
model.CloneFromSiteId,
model.CloneToSiteId,
post.Id
);
if (!string.IsNullOrWhiteSpace(postId)) postCount++;
}
}
catch (Exception ex)
{
this.AlertDanger(string.Format(sr["An error occurred while cloning blog posts. Only {0}/{1} were copied."],postCount, model.CloneFromPostCount), true);
this.AlertDanger(ex.Message, true);
}
if(postCount == model.CloneFromPostCount)
{
this.AlertSuccess(sr["Blog post cloning was successful!"], true);
}
}
if(model.RewriteContentUrls)
{
this.AlertInformation(sr["Rewriting content urls is not yet implemented."], true);
}
await _treeCache.ClearTreeCache();
return View(model);
}
private async Task PopulateAndValidateModel(ContentCloningViewModel model)
{
bool isServerAdminSite = _siteManager.CurrentSite.IsServerAdminSite;
string currentSiteId = _siteManager.CurrentSite.Id.ToString();
if(isServerAdminSite)
{
//if we are on a specific site settings page then we can preselect the site to clone to
if(!string.IsNullOrWhiteSpace(model.SiteId))
{
model.CloneToSiteId = model.SiteId;
model.AllowCloneToSiteSelection = false;
}
}
else
{ //can't allow destination site selection if we are not on the server admin site
model.SiteId = currentSiteId;
model.CloneToSiteId = currentSiteId;
model.AllowCloneToSiteSelection = false;
}
bool useFolderNames = _configuration.GetSection("MultiTenantOptions").GetValue("Mode") == "FolderName";
List sites = await _siteQueries.GetList();
//build the sites list for To and From, excluding any site selected in the other list
foreach(var site in sites)
{
string url = string.Empty;
if(useFolderNames) url = "/" + site.SiteFolderName;
else url = site.PreferredHostName;
bool addTo = true;
bool addFrom = true;
if(!string.IsNullOrWhiteSpace(model.CloneToSiteId) &&
model.CloneToSiteId == site.Id.ToString())
{
addFrom= false;
model.CloneToSiteName = site.SiteName;
model.CloneToPageCount = await _pageQueries.GetCount(model.CloneToSiteId, true);
model.CloneToPostCount = await _postQueries.GetCount(model.CloneToSiteId, null, true);
}
if (!string.IsNullOrWhiteSpace(model.CloneFromSiteId) &&
model.CloneFromSiteId == site.Id.ToString())
{
addTo = false;
model.CloneFromSiteName = site.SiteName;
model.CloneFromPageCount = await _pageQueries.GetCount(model.CloneFromSiteId, true);
model.CloneFromPostCount = await _postQueries.GetCount(model.CloneFromSiteId, null, true);
}
if (addTo)
{
model.CloneToSites.Add(new ContentCloningViewModel.SiteDetails
{
SiteId = site.Id.ToString(),
SiteIdentifier = site.SiteName + " (" + url + ") [ " + site.Id + " ]"
});
}
if(addFrom)
{
model.CloneFromSites.Add(new ContentCloningViewModel.SiteDetails
{
SiteId = site.Id.ToString(),
SiteIdentifier = site.SiteName + " (" + url + ") [ " + site.Id + " ]"
});
}
}
model.CloneAllowed = true;
if(string.IsNullOrWhiteSpace(model.CloneToSiteId))
{
model.CloneAllowed = false;
this.AlertDanger(sr["Please select a destination site!"], true);
}
else
{
if (model.ClonePages && model.CloneToPageCount > 0)
{
model.CloneAllowed = false;
if(model.AllowCloneToSiteSelection)
{
this.AlertWarning(
string.Format(sr["The destination site you have chosen already contains {0} content pages!"], model.CloneToPageCount),
true);
}
else
{
this.AlertWarning(
string.Format(sr["This site already contains {0} content pages!"], model.CloneToPageCount),
true);
}
}
if (model.CloneBlogPosts && model.CloneToPostCount > 0)
{
model.CloneAllowed = false;
if (model.AllowCloneToSiteSelection)
{
this.AlertWarning(
string.Format(sr["The destination site you have chosen already contains {0} blog posts!"], model.CloneToPostCount),
true);
}
else
{
this.AlertWarning(
string.Format(sr["This site already contains {0} blog posts!"], model.CloneToPostCount),
true);
}
}
}
if(model.CloneAllowed)
{
if(string.IsNullOrWhiteSpace(model.CloneFromSiteId))
{
model.CloneAllowed = false;
this.AlertDanger(sr["Please select a source site!"], true);
}
else
{
if (model.CloneFromPageCount == 0 && model.CloneFromPostCount == 0)
{
model.CloneAllowed = false;
this.AlertWarning(
sr["The source site you have chosen does not contain any content pages or blog posts!"],
true);
}
else
{
this.AlertInformation(
string.Format(sr["The source site you have chosen contains {0} content pages and {1} blog posts."], model.CloneFromPageCount, model.CloneFromPostCount),
true);
}
}
}
else
{
if(!string.IsNullOrWhiteSpace(model.CloneToSiteId))
this.AlertDanger(sr["Content Cloning is not possible for this site."], true);
}
return model;
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/Controllers/ContentSettingsController.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-08-07
// Last Modified: 2019-03-04
//
using cloudscribe.Core.Models;
using cloudscribe.Core.SimpleContent.Integration.ViewModels;
using cloudscribe.SimpleContent.Models;
using cloudscribe.SimpleContent.Web.Services;
using cloudscribe.Web.Common.Extensions;
using cloudscribe.Web.Navigation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Integration.Mvc.Controllers
{
public class ContentSettingsController : Controller
{
public ContentSettingsController(
IProjectService projectService,
IAuthorizationService authorizationService,
IUserQueries userQueries,
ITeaserService teaserService,
NavigationTreeBuilderService navigationTreeBuilderService,
IStringLocalizer localizer
)
{
this.projectService = projectService;
this.authorizationService = authorizationService;
this.userQueries = userQueries;
sr = localizer;
if(teaserService is TeaserServiceDisabled)
{
_teasersDisabled = true;
}
_navigationTreeBuilderService = navigationTreeBuilderService;
}
private IProjectService projectService;
private IAuthorizationService authorizationService;
private IUserQueries userQueries;
private bool _teasersDisabled = false;
private IStringLocalizer sr;
private readonly NavigationTreeBuilderService _navigationTreeBuilderService;
[Authorize(Policy = "AdminPolicy")]
// GET: /ContentSettings
[HttpGet]
public async Task Index()
{
ViewData["Title"] = sr["Content Settings"];
var projectSettings = await projectService.GetCurrentProjectSettings();
var model = new ContentSettingsViewModel();
model.ChannelCategoriesCsv = projectSettings.ChannelCategoriesCsv;
//model.ChannelRating = projectSettings.ChannelRating;
//model.ChannelTimeToLive = projectSettings.ChannelTimeToLive;
model.CommentNotificationEmail = projectSettings.CommentNotificationEmail;
model.DaysToComment = projectSettings.DaysToComment;
model.Description = projectSettings.Description;
model.IncludePubDateInPostUrls = projectSettings.IncludePubDateInPostUrls;
model.LanguageCode = projectSettings.LanguageCode;
model.ManagingEditorEmail = projectSettings.ManagingEditorEmail;
model.ModerateComments = projectSettings.ModerateComments;
model.PostsPerPage = projectSettings.PostsPerPage;
model.PubDateFormat = projectSettings.PubDateFormat;
//model.RemoteFeedProcessorUseAgentFragment = projectSettings.RemoteFeedProcessorUseAgentFragment;
model.RemoteFeedUrl = projectSettings.RemoteFeedUrl;
model.ShowTitle = projectSettings.ShowTitle;
model.Title = projectSettings.Title; //aka Blog Page Title
//model.UseMetaDescriptionInFeed = projectSettings.UseMetaDescriptionInFeed;
model.WebmasterEmail = projectSettings.WebmasterEmail;
model.Publisher = projectSettings.Publisher;
model.PublisherLogoUrl = projectSettings.PublisherLogoUrl;
model.PublisherLogoHeight = projectSettings.PublisherLogoHeight;
model.PublisherLogoWidth = projectSettings.PublisherLogoWidth;
model.PublisherEntityType = projectSettings.PublisherEntityType;
model.DisqusShortName = projectSettings.DisqusShortName;
model.PostsPerPage = projectSettings.PostsPerPage;
model.BlogMenuLinksToNewestPost = projectSettings.BlogMenuLinksToNewestPost;
model.DefaultPageSlug = projectSettings.DefaultPageSlug;
model.ShowRecentPostsOnDefaultPage = projectSettings.ShowRecentPostsOnDefaultPage;
model.ShowFeaturedPostsOnDefaultPage = projectSettings.ShowFeaturedPostsOnDefaultPage;
model.AddBlogToPagesTree = projectSettings.AddBlogToPagesTree;
model.BlogPagePosition = projectSettings.BlogPagePosition;
model.BlogPageText = projectSettings.BlogPageText;
model.BlogPageNavComponentVisibility = projectSettings.BlogPageNavComponentVisibility;
model.LocalMediaVirtualPath = projectSettings.LocalMediaVirtualPath;
model.CdnUrl = projectSettings.CdnUrl;
model.FacebookAppId = projectSettings.FacebookAppId;
model.SiteName = projectSettings.SiteName;
model.TwitterCreator = projectSettings.TwitterCreator;
model.TwitterPublisher = projectSettings.TwitterPublisher;
model.DefaultContentType = projectSettings.DefaultContentType;
model.TeasersDisabled = _teasersDisabled;
model.TeaserMode = projectSettings.TeaserMode;
model.TeaserTruncationMode = projectSettings.TeaserTruncationMode;
model.TeaserTruncationLength = projectSettings.TeaserTruncationLength;
model.DefaultFeedItems = projectSettings.DefaultFeedItems;
model.MaxFeedItems = projectSettings.MaxFeedItems;
model.AboutContent = projectSettings.AboutContent;
model.AboutHeading = projectSettings.AboutHeading;
model.ShowAboutBox = projectSettings.ShowAboutBox;
model.ShowRelatedPosts = projectSettings.ShowRelatedPosts;
model.ShowArchivedPosts = projectSettings.ShowArchivedPosts;
model.ShowBlogCategories = projectSettings.ShowBlogCategories;
model.ShowCreatedBy = projectSettings.ShowCreatedBy;
model.ShowCreatedDate = projectSettings.ShowCreatedDate;
model.ShowLastModifiedBy = projectSettings.ShowLastModifiedBy;
model.ShowLastModifiedDate = projectSettings.ShowLastModifiedDate;
bool canManageUsers = false;
try
{
var result = await authorizationService.AuthorizeAsync(User, "UserManagementPolicy");
canManageUsers = result.Succeeded;
}
catch (InvalidOperationException) { } // thrown if policy doesn't exist
if(canManageUsers)
{
var projectId = projectSettings.Id;
if(projectId.Length > 36)
{
projectId = projectId.Substring(0, 36);
}
var editors = new List();
var contentEditors = await userQueries.GetUsersForClaim(
new Guid(projectId),
ProjectConstants.ContentEditorClaimType,
projectSettings.Id
);
if(contentEditors != null)
{
editors.AddRange(contentEditors);
}
var blogEditors = await userQueries.GetUsersForClaim(
new Guid(projectId),
ProjectConstants.BlogEditorClaimType,
projectSettings.Id
);
if(blogEditors != null)
{
editors.AddRange(blogEditors);
}
var pageEditors = await userQueries.GetUsersForClaim(
new Guid(projectId),
ProjectConstants.PageEditorClaimType,
projectSettings.Id
);
if (pageEditors != null)
{
editors.AddRange(pageEditors);
}
foreach(var e in editors)
{
if(!model.Editors.Any(x => x.Id == e.Id))
{
model.Editors.Add(e);
}
}
//model.Editors.AddRange(editors.Distinct());
}
return View(model);
}
[Authorize(Policy = "AdminPolicy")]
[HttpPost]
public async Task Index(ContentSettingsViewModel model)
{
ViewData["Title"] = sr["Content Settings"];
if (!ModelState.IsValid)
{
model.TeasersDisabled = _teasersDisabled;
return View(model);
}
var projectSettings = await projectService.GetCurrentProjectSettings();
projectSettings.ChannelCategoriesCsv = model.ChannelCategoriesCsv;
//projectSettings.ChannelRating = model.ChannelRating;
//projectSettings.ChannelTimeToLive = model.ChannelTimeToLive;
projectSettings.CommentNotificationEmail = model.CommentNotificationEmail;
projectSettings.DaysToComment = model.DaysToComment;
projectSettings.Description = model.Description;
projectSettings.IncludePubDateInPostUrls = model.IncludePubDateInPostUrls;
projectSettings.LanguageCode = model.LanguageCode;
projectSettings.ManagingEditorEmail = model.ManagingEditorEmail;
projectSettings.ModerateComments = model.ModerateComments;
projectSettings.PostsPerPage = model.PostsPerPage;
projectSettings.PubDateFormat = model.PubDateFormat;
//projectSettings.RemoteFeedProcessorUseAgentFragment = model.RemoteFeedProcessorUseAgentFragment;
projectSettings.RemoteFeedUrl = model.RemoteFeedUrl;
projectSettings.ShowTitle = model.ShowTitle;
projectSettings.Title = model.Title;
//projectSettings.UseMetaDescriptionInFeed = model.UseMetaDescriptionInFeed;
projectSettings.WebmasterEmail = model.WebmasterEmail;
projectSettings.Publisher = model.Publisher;
projectSettings.PublisherLogoUrl = model.PublisherLogoUrl;
projectSettings.PublisherLogoWidth = model.PublisherLogoWidth;
projectSettings.PublisherLogoHeight = model.PublisherLogoHeight;
projectSettings.PublisherEntityType = model.PublisherEntityType;
projectSettings.DisqusShortName = model.DisqusShortName;
projectSettings.ShowRecentPostsOnDefaultPage = model.ShowRecentPostsOnDefaultPage;
projectSettings.ShowFeaturedPostsOnDefaultPage = model.ShowFeaturedPostsOnDefaultPage;
bool needToClearMenuCache = false;
if (model.BlogMenuLinksToNewestPost != projectSettings.BlogMenuLinksToNewestPost) needToClearMenuCache = true;
if (model.DefaultPageSlug != projectSettings.DefaultPageSlug) needToClearMenuCache = true;
if (model.AddBlogToPagesTree != projectSettings.AddBlogToPagesTree) needToClearMenuCache = true;
if (model.BlogPagePosition != projectSettings.BlogPagePosition) needToClearMenuCache = true;
if (model.BlogPageText != projectSettings.BlogPageText) needToClearMenuCache = true;
if (model.BlogPageNavComponentVisibility != projectSettings.BlogPageNavComponentVisibility) needToClearMenuCache = true;
projectSettings.BlogMenuLinksToNewestPost = model.BlogMenuLinksToNewestPost;
projectSettings.DefaultPageSlug = model.DefaultPageSlug;
projectSettings.BlogPagePosition = model.BlogPagePosition;
projectSettings.AddBlogToPagesTree = model.AddBlogToPagesTree;
projectSettings.BlogPageText = model.BlogPageText;
projectSettings.BlogPageNavComponentVisibility = model.BlogPageNavComponentVisibility;
projectSettings.LocalMediaVirtualPath = model.LocalMediaVirtualPath;
projectSettings.CdnUrl = model.CdnUrl;
projectSettings.FacebookAppId = model.FacebookAppId;
projectSettings.SiteName = model.SiteName;
projectSettings.TwitterPublisher = model.TwitterPublisher;
projectSettings.TwitterCreator = model.TwitterCreator;
projectSettings.DefaultContentType = model.DefaultContentType;
projectSettings.TeaserMode = model.TeaserMode;
projectSettings.TeaserTruncationLength = model.TeaserTruncationLength;
projectSettings.TeaserTruncationMode = model.TeaserTruncationMode;
projectSettings.DefaultFeedItems = model.DefaultFeedItems;
projectSettings.MaxFeedItems = model.MaxFeedItems;
projectSettings.AboutContent = model.AboutContent;
projectSettings.AboutHeading = model.AboutHeading;
projectSettings.ShowAboutBox = model.ShowAboutBox;
projectSettings.ShowRelatedPosts = model.ShowRelatedPosts;
projectSettings.ShowArchivedPosts = model.ShowArchivedPosts;
projectSettings.ShowBlogCategories = model.ShowBlogCategories;
projectSettings.ShowCreatedBy = model.ShowCreatedBy;
projectSettings.ShowCreatedDate = model.ShowCreatedDate;
projectSettings.ShowLastModifiedBy = model.ShowLastModifiedBy;
projectSettings.ShowLastModifiedDate = model.ShowLastModifiedDate;
await projectService.Update(projectSettings);
if(needToClearMenuCache)
{
//projectService.ClearNavigationCache();
await _navigationTreeBuilderService.ClearTreeCache();
}
this.AlertSuccess(sr["Content Settings were successfully updated."], true);
return RedirectToAction("Index");
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/Controllers/CultureMetaweblogController.cs
================================================
using cloudscribe.MetaWeblog;
using cloudscribe.MetaWeblog.Controllers;
using cloudscribe.MetaWeblog.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Controllers
{
public class CultureMetaweblogController : MetaWeblogController
{
public CultureMetaweblogController(
IWebHostEnvironment appEnv,
IMetaWeblogRequestParser metaWeblogRequestParser,
IMetaWeblogRequestProcessor metaWeblogProcessor,
IMetaWeblogResultFormatter metaWeblogResultFormatter,
IMetaWeblogSecurity metaWeblogSecurity,
IMetaWeblogRequestValidator metaWebLogRequestValidator,
ILogger logger,
IOptions optionsAccessor = null
) : base(appEnv, metaWeblogRequestParser, metaWeblogProcessor, metaWeblogResultFormatter, metaWeblogSecurity, metaWebLogRequestValidator, logger, optionsAccessor)
{
}
[HttpPost]
//[Route("{folder:sitefolder}/api/metaweblog")]
public override async Task Index()
{
return await base.Index();
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/Controllers/CultureRssController.cs
================================================
using cloudscribe.Syndication.Models.Rss;
using cloudscribe.Syndication.Web.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Controllers
{
public class CultureRssController : RssController
{
public CultureRssController(
ILogger logger,
IEnumerable channelProviders = null,
IChannelProviderResolver channelResolver = null,
IXmlFormatter xmlFormatter = null
) : base(logger, channelProviders, channelResolver, xmlFormatter)
{
}
[HttpGet]
[ResponseCache(CacheProfileName = "RssCacheProfile")]
//[Route("{folder:sitefolder}/api/rss")]
public override async Task Index()
{
return await base.Index();
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/Controllers/CultureSiteMapController.cs
================================================
using cloudscribe.Web.SiteMap;
using cloudscribe.Web.SiteMap.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Controllers
{
public class CultureSiteMapController : SiteMapController
{
public CultureSiteMapController(
ILogger logger,
IEnumerable nodeProviders = null
):base(logger, nodeProviders)
{
}
[HttpGet]
[ResponseCache(CacheProfileName = "SiteMapCacheProfile")]
public override async Task Index()
{
return await base.Index();
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/Controllers/FolderMetaweblogController.cs
================================================
using cloudscribe.MetaWeblog;
using cloudscribe.MetaWeblog.Controllers;
using cloudscribe.MetaWeblog.Models;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Controllers
{
public class FolderMetaweblogController : MetaWeblogController
{
public FolderMetaweblogController(
IWebHostEnvironment appEnv,
IMetaWeblogRequestParser metaWeblogRequestParser,
IMetaWeblogRequestProcessor metaWeblogProcessor,
IMetaWeblogResultFormatter metaWeblogResultFormatter,
IMetaWeblogSecurity metaWeblogSecurity,
IMetaWeblogRequestValidator metaWebLogRequestValidator,
ILogger logger,
IOptions optionsAccessor = null
):base(appEnv, metaWeblogRequestParser, metaWeblogProcessor, metaWeblogResultFormatter, metaWeblogSecurity, metaWebLogRequestValidator, logger, optionsAccessor)
{
}
[HttpPost]
//[Route("{folder:sitefolder}/api/metaweblog")]
public override async Task Index()
{
return await base.Index();
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/Controllers/FolderRssController.cs
================================================
using cloudscribe.Syndication.Models.Rss;
using cloudscribe.Syndication.Web.Controllers;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Controllers
{
public class FolderRssController : RssController
{
public FolderRssController(
ILogger logger,
IEnumerable channelProviders = null,
IChannelProviderResolver channelResolver = null,
IXmlFormatter xmlFormatter = null
):base(logger, channelProviders, channelResolver, xmlFormatter)
{
}
[HttpGet]
[ResponseCache(CacheProfileName = "RssCacheProfile")]
//[Route("{folder:sitefolder}/api/rss")]
public override async Task Index()
{
return await base.Index();
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/CoreProjectEmailService.cs
================================================
using cloudscribe.Core.Models;
using cloudscribe.Core.SimpleContent.ViewModels;
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2018-03-15
// Last Modified: 2018-03-15
//
using cloudscribe.Email;
using cloudscribe.SimpleContent.Models;
using cloudscribe.Web.Common.Razor;
using cloudscribe.Core.Web.Components;
using Microsoft.Extensions.Logging;
using System;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent
{
public class CoreProjectEmailService : IProjectEmailService
{
public CoreProjectEmailService(
ViewRenderer viewRenderer,
ISiteContextResolver siteResolver,
IEmailSenderResolver emailSenderResolver,
ILogger logger
)
{
_viewRenderer = viewRenderer;
_emailSenderResolver = emailSenderResolver;
_siteResolver = siteResolver;
_log = logger;
}
private ViewRenderer _viewRenderer;
private ISiteContextResolver _siteResolver;
private IEmailSenderResolver _emailSenderResolver;
private ILogger _log;
public async Task SendCommentNotificationEmailAsync(
IProjectSettings project,
IPost post,
IComment comment,
string postUrl,
string approveUrl,
string deleteUrl
)
{
var sender = await _emailSenderResolver.GetEmailSender(project.Id);
if (sender == null)
{
var logMessage = $"failed to send account confirmation email because email settings are not populated for site {project.Title}";
_log.LogError(logMessage);
return;
}
if (string.IsNullOrWhiteSpace(project.CommentNotificationEmail))
{
var logMessage = $"failed to send comment notification email because CommentNotificationEmail is not populated for project {project.Id}";
_log.LogError(logMessage);
return;
}
var site = await _siteResolver.GetById(new Guid(project.Id));
var model = new CoreCommentNotificationModel(site, project, post, comment, postUrl);
var subject = "Blog comment: " + post.Title;
string plainTextMessage = null;
string htmlMessage = null;
try
{
try
{
htmlMessage
= await _viewRenderer.RenderViewAsString("CommentNotificationEmail", model);
}
catch (Exception ex)
{
_log.LogError("error generating html email from razor template", ex);
return;
}
await sender.SendEmailAsync(
project.CommentNotificationEmail, //to
site.DefaultEmailFromAddress, //from
subject,
plainTextMessage,
htmlMessage,
comment.Email, //replyto
configLookupKey: project.Id
).ConfigureAwait(false);
}
catch (Exception ex)
{
_log.LogError($"error sending comment notification email {ex.Message} : {ex.StackTrace}");
}
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/CustomClaimProvider.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using cloudscribe.Core.Models;
using System.Security.Claims;
using Microsoft.Extensions.Options;
using cloudscribe.Core.Identity;
namespace cloudscribe.Core.SimpleContent.Integration
{
public class CustomClaimProvider : ICustomClaimProvider
{
public CustomClaimProvider(
IOptions> claimMapsAccessor
)
{
claimMaps = claimMapsAccessor.Value;
}
private List claimMaps;
public Task AddClaims(SiteUser user, ClaimsIdentity identity)
{
foreach(var map in claimMaps)
{
if(map.UserEmail == user.Email)
{
foreach(var c in map.Claims)
{
identity.AddClaim(new Claim(c.ClaimType, c.ClaimValue));
}
}
}
return Task.FromResult(0);
}
}
public class CustomClaimMap
{
public string UserEmail { get; set; }
public List Claims { get; set; }
}
public class CustomClaim
{
public string ClaimType { get; set; }
public string ClaimValue { get; set; }
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/EventHandlers/HandleRoleDeletred.cs
================================================
using cloudscribe.Core.Models;
using cloudscribe.Core.Models.EventHandlers;
using cloudscribe.SimpleContent.Models;
using cloudscribe.Web.Navigation;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.EventHandlers
{
public class HandleRoleDeleted : IHandleRoleDeleted
{
private readonly IPageService _pageService;
private readonly NavigationTreeBuilderService _nav;
public HandleRoleDeleted(IPageService pageService,
NavigationTreeBuilderService nav)
{
_pageService = pageService;
_nav = nav;
}
async Task IHandleRoleDeleted.Handle(ISiteRole role)
{
var pages = await _pageService.GetAllPages(role.SiteId.ToString());
int pagesUpdated = 0;
foreach (var page in pages.Where(p => !string.IsNullOrWhiteSpace(p.ViewRoles)))
{
// assuming no case sensitivity issues here
if (page.ViewRoles.Contains(role.RoleName))
{
var rolesStrings = page.ViewRoles.Split(',');
var newRolesString = new StringBuilder();
foreach (var roleName in rolesStrings)
{
if (!roleName.Trim().Equals(role.RoleName.Trim()))
{
newRolesString.Append(roleName.Trim());
newRolesString.Append(",");
}
}
page.ViewRoles = newRolesString.ToString().TrimEnd(',');
await _pageService.Update(page);
pagesUpdated++;
}
}
if (pagesUpdated > 0)
await _nav.GetTree();
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/EventHandlers/HandleRoleUpdated.cs
================================================
using cloudscribe.Core.Models;
using cloudscribe.Core.Models.EventHandlers;
using cloudscribe.SimpleContent.Models;
using cloudscribe.Web.Navigation;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.EventHandlers
{
public class HandleRoleUpdated : IHandleRoleUpdated
{
private readonly IPageService _pageService;
private readonly NavigationTreeBuilderService _nav;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ILogger _logger;
public HandleRoleUpdated(IPageService pageService,
NavigationTreeBuilderService nav,
IHttpContextAccessor httpContextAccessor,
ILogger logger)
{
_pageService = pageService;
_nav = nav;
_httpContextAccessor = httpContextAccessor;
_logger = logger;
}
async Task IHandleRoleUpdated.Handle(ISiteRole role, string oldRoleName)
{
var pages = await _pageService.GetAllPages(role.SiteId.ToString());
int pagesUpdated = 0;
foreach (var page in pages.Where(p => !string.IsNullOrWhiteSpace(p.ViewRoles)))
{
// assuming no case sensitivity issues here
if (page.ViewRoles.Contains(oldRoleName))
{
var rolesStrings = page.ViewRoles.Split(',');
var newRolesString = new StringBuilder();
foreach (var roleName in rolesStrings)
{
if (roleName.Trim().Equals(oldRoleName.Trim()))
newRolesString.Append(role.RoleName);
else
newRolesString.Append(roleName.Trim());
newRolesString.Append(",");
}
page.ViewRoles = newRolesString.ToString().TrimEnd(',');
await _pageService.Update(page);
pagesUpdated++;
}
}
///////////////////////////////////
// This is an attempt to fiddle the navigation cache and the
// roles of the current user so that they remain in step with one another
// after a user renames a role that they themselves are in.
// Otherwise pages can mysteriously vanish from the nav when they should not do,
// following a role rename - jk
if (pagesUpdated > 0)
{
try
{
var user = _httpContextAccessor?.HttpContext?.User;
if (user != null && user.Identity != null && user.Identity.IsAuthenticated && user.IsInRole(oldRoleName))
{
var claims = new List
{
new Claim(ClaimTypes.Role, role.RoleName)
};
var appIdentity = new ClaimsIdentity(claims);
user.AddIdentity(appIdentity);
}
await _nav.GetTree();
}
catch (Exception ex)
{
_logger.LogError(ex, "Error re-setting user claims and cache after role update");
}
}
////////////////////////////////////////
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/MultiTenantBlogRoutes.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-08-06
// Last Modified: 2019-03-04
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Models;
using cloudscribe.SimpleContent.Web.Services;
using Microsoft.Extensions.Options;
namespace cloudscribe.Core.SimpleContent.Integration
{
public class MultiTenantBlogRoutes : IBlogRoutes
{
public MultiTenantBlogRoutes(
SiteContext currentSite,
IOptions multiTenantOptionsAccessor,
CultureHelper cultureHelper
)
{
_currentSite = currentSite;
_multiTenantOptions = multiTenantOptionsAccessor.Value;
_cultureHelper = cultureHelper;
}
private readonly SiteContext _currentSite;
private readonly MultiTenantOptions _multiTenantOptions;
private readonly CultureHelper _cultureHelper;
public string PostWithDateRouteName
{
get
{
if(_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if(!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPostWithDateRouteName;
}
return ProjectConstants.FolderPostWithDateRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePostWithDateRouteName;
}
return ProjectConstants.PostWithDateRouteName;
}
}
public string PostWithoutDateRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPostWithoutDateRouteName;
}
return ProjectConstants.FolderPostWithoutDateRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePostWithoutDateRouteName;
}
return ProjectConstants.PostWithoutDateRouteName;
}
}
public string MostRecentPostRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderMostRecentPostRouteName;
}
return ProjectConstants.FolderMostRecentPostRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureMostRecentPostRouteName;
}
return ProjectConstants.MostRecentPostRouteName;
}
}
public string BlogCategoryRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderBlogCategoryRouteName;
}
return ProjectConstants.FolderBlogCategoryRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureBlogCategoryRouteName;
}
return ProjectConstants.BlogCategoryRouteName;
}
}
public string BlogArchiveRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderBlogArchiveRouteName;
}
return ProjectConstants.FolderBlogArchiveRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureBlogArchiveRouteName;
}
return ProjectConstants.BlogArchiveRouteName;
}
}
public string NewPostRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderNewPostRouteName;
}
return ProjectConstants.FolderNewPostRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureNewPostRouteName;
}
return ProjectConstants.NewPostRouteName;
}
}
public string BlogIndexRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderBlogIndexRouteName;
}
return ProjectConstants.FolderBlogIndexRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureBlogIndexRouteName;
}
return ProjectConstants.BlogIndexRouteName;
}
}
public string PostEditRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPostEditRouteName;
}
return ProjectConstants.FolderPostEditRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePostEditRouteName;
}
return ProjectConstants.PostEditRouteName;
}
}
public string PostEditWithTemplateRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPostEditWithTemplateRouteName;
}
return ProjectConstants.FolderPostEditWithTemplateRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePostEditWithTemplateRouteName;
}
return ProjectConstants.PostEditWithTemplateRouteName;
}
}
public string PostDeleteRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPostDeleteRouteName;
}
return ProjectConstants.FolderPostDeleteRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePostDeleteRouteName;
}
return ProjectConstants.PostDeleteRouteName;
}
}
public string PostHistoryRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPostHistoryRouteName;
}
return ProjectConstants.FolderPostHistoryRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePostHistoryRouteName;
}
return ProjectConstants.PostHistoryRouteName;
}
}
public string CanEditRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderBlogCanEditRouteName;
}
return ProjectConstants.FolderBlogCanEditRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureBlogCanEditRouteName;
}
return ProjectConstants.BlogCanEditRouteName;
}
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/MultiTenantPageRoutes.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2017-01-08
// Last Modified: 2019-07-03
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Models;
using cloudscribe.SimpleContent.Web.Services;
using Microsoft.Extensions.Options;
namespace cloudscribe.Core.SimpleContent.Integration
{
public class MultiTenantPageRoutes : IPageRoutes
{
public MultiTenantPageRoutes(
SiteContext currentSite,
IOptions multiTenantOptionsAccessor,
CultureHelper cultureHelper
)
{
_currentSite = currentSite;
_multiTenantOptions = multiTenantOptionsAccessor.Value;
_cultureHelper = cultureHelper;
}
private SiteContext _currentSite;
private MultiTenantOptions _multiTenantOptions;
private readonly CultureHelper _cultureHelper;
public string PageRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPageIndexRouteName;
}
return ProjectConstants.FolderPageIndexRouteName;
}
}
if(_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePageIndexRouteName;
}
return ProjectConstants.PageIndexRouteName;
}
}
public string PageEditRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPageEditRouteName;
}
return ProjectConstants.FolderPageEditRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePageEditRouteName;
}
return ProjectConstants.PageEditRouteName;
}
}
public string PageEditWithTemplateRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPageEditWithTemplateRouteName;
}
return ProjectConstants.FolderPageEditWithTemplateRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePageEditWithTemplateRouteName;
}
return ProjectConstants.PageEditWithTemplateRouteName;
}
}
public string NewPageRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderNewPageRouteName;
}
return ProjectConstants.FolderNewPageRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureNewPageRouteName;
}
return ProjectConstants.NewPageRouteName;
}
}
public string PageDeleteRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPageDeleteRouteName;
}
return ProjectConstants.FolderPageDeleteRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePageDeleteRouteName;
}
return ProjectConstants.PageDeleteRouteName;
}
}
public string PageDevelopRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPageDevelopRouteName;
}
return ProjectConstants.FolderPageDevelopRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePageDevelopRouteName;
}
return ProjectConstants.PageDevelopRouteName;
}
}
public string PageTreeRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPageTreeRouteName;
}
return ProjectConstants.FolderPageTreeRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePageTreeRouteName;
}
return ProjectConstants.PageTreeRouteName;
}
}
public string PageHistoryRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPageHistoryRouteName;
}
return ProjectConstants.FolderPageHistoryRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePageHistoryRouteName;
}
return ProjectConstants.PageHistoryRouteName;
}
}
public string CanEditRouteName
{
get
{
if (_multiTenantOptions.Mode == MultiTenantMode.FolderName)
{
if (!string.IsNullOrEmpty(_currentSite.SiteFolderName))
{
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CultureFolderPageCanEditRouteName;
}
return ProjectConstants.FolderPageCanEditRouteName;
}
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return ProjectConstants.CulturePageCanEditRouteName;
}
return ProjectConstants.PageCanEditRouteName;
}
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/ProjectSecurityResolver.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-07-11
// Last Modified: 2016-08-13
//
using cloudscribe.Core.Identity;
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Web;
using cloudscribe.SimpleContent.Models;
using Microsoft.AspNetCore.Authorization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
namespace cloudscribe.Core.SimpleContent.Integration
{
public class ProjectSecurityResolver : IProjectSecurityResolver
{
public ProjectSecurityResolver(
SiteUserManager userManager,
SignInManager signInManager,
IProjectSettingsResolver projectResolver,
IAuthorizationService authorizationService
)
{
this.userManager = userManager;
this.signInManager = signInManager;
this.projectResolver = projectResolver;
this.authorizationService = authorizationService;
}
private SiteUserManager userManager;
private SignInManager signInManager;
private IAuthorizationService authorizationService;
private IProjectSettingsResolver projectResolver;
public async Task ValidatePermissions(
string projectId,
string userName,
string providedPassword,
CancellationToken cancellationToken)
{
var displayName = string.Empty;
var isAuthenticated = false;
var canEditPosts = false;
var canEditPages = false;
var timeZoneId = userManager.Site.TimeZoneId;
var authUser = await userManager.FindByNameAsync(userName);
if (authUser != null)
{
isAuthenticated = await userManager.CheckPasswordAsync(authUser, providedPassword);
}
if (isAuthenticated)
{
var claimsPrincipal = await signInManager.CreateUserPrincipalAsync(authUser);
if (string.IsNullOrEmpty(projectId))
{
projectId = claimsPrincipal.GetProjectId();
}
if (string.IsNullOrEmpty(projectId))
{
var project = await projectResolver.GetCurrentProjectSettings(cancellationToken);
if (project != null) projectId = project.Id;
}
if (!string.IsNullOrEmpty(projectId))
{
canEditPosts = await claimsPrincipal.CanEditBlog(projectId, authorizationService);
canEditPages = await claimsPrincipal.CanEditPages(projectId, authorizationService);
}
//displayName = claimsPrincipal.GetDisplayName();
displayName = claimsPrincipal.Identity.Name;
if(!string.IsNullOrWhiteSpace(authUser.TimeZoneId))
{
timeZoneId = authUser.TimeZoneId;
}
}
var blogSecurity = new ProjectSecurityResult(displayName, projectId, isAuthenticated, canEditPosts, canEditPages, timeZoneId);
return blogSecurity;
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/README.md
================================================
# cloudscribe.Core.SimpleContent
[](https://www.nuget.org/packages/cloudscribe.Core.SimpleContent)
[](https://opensource.org/licenses/Apache-2.0)
Core library for the cloudscribe SimpleContent CMS and blog engine for ASP.NET Core.
## Installation
```shell
Install-Package cloudscribe.Core.SimpleContent
```
## Usage
Reference this package to add core content and blog features to your ASP.NET Core application.
## Contributing
Contributions are welcome! Please see the [contributing guidelines](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/CONTRIBUTING.md).
## License
Licensed under the Apache 2.0 License. See the [LICENSE](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/LICENSE) file for details.
================================================
FILE: src/cloudscribe.Core.SimpleContent/Resources.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Integration
{
///
/// a class to represent localization resources for controllers and views
///
public class SimpleContentIntegration
{
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/SiteFileSystemMediaProcessor.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2017-05-28
// Last Modified: 2018-07-09
//
using cloudscribe.Core.Models;
using cloudscribe.FileManager.Web.Models;
using cloudscribe.SimpleContent.Models;
using cloudscribe.SimpleContent.Services;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using System.IO;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Integration
{
public class SiteFileSystemMediaProcessor : FileSystemMediaProcessor, IMediaProcessor
{
public SiteFileSystemMediaProcessor(
SiteContext currentSite,
IMediaPathResolver mediaPathResolver,
ILogger logger,
IWebHostEnvironment env
):base(logger, env)
{
_currentSite = currentSite;
_mediaPathResolver = mediaPathResolver;
}
private readonly SiteContext _currentSite;
private IMediaPathResolver _mediaPathResolver;
private MediaRootPathInfo rootPath;
private async Task EnsurePathInfo()
{
if (rootPath != null) { return; }
rootPath = await _mediaPathResolver.Resolve().ConfigureAwait(false);
if (rootPath != null) { return; }
}
private async Task EnsureFsPath(string mediaVirtualPath)
{
await EnsurePathInfo();
var fsPath = rootPath.RootFileSystemPath + mediaVirtualPath.Replace('/', Path.DirectorySeparatorChar);
if (Directory.Exists(fsPath)) return; //nothing to do
var segments = mediaVirtualPath.Split('/');
EnsureFolderPaths(rootPath.RootFileSystemPath, segments);
}
public override async Task ResolveMediaUrl(string mediaVirtualPath, string fileName)
{
var result = await base.ResolveMediaUrl(mediaVirtualPath, fileName);
if(!string.IsNullOrWhiteSpace(_currentSite.SiteFolderName))
{
return "/" + _currentSite.SiteFolderName + result;
}
return result;
}
public override async Task SaveMedia(string mediaVirtualPath, string fileName, byte[] bytes)
{
await EnsureFsPath(mediaVirtualPath);
var newUrl = mediaVirtualPath + fileName;
var fsPath = rootPath.RootFileSystemPath + newUrl.Replace('/', Path.DirectorySeparatorChar);
File.WriteAllBytes(fsPath, bytes);
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/SiteNavigationCacheKeyResolver.cs
================================================
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Web.Services;
using cloudscribe.Web.Navigation;
using cloudscribe.Web.Navigation.Caching;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent
{
public class SiteNavigationCacheKeyResolver : ITreeCacheKeyResolver
{
public SiteNavigationCacheKeyResolver(
SiteContext currentSite,
CultureHelper cultureHelper
)
{
_currentSite = currentSite;
_cultureHelper = cultureHelper;
}
private readonly SiteContext _currentSite;
private readonly CultureHelper _cultureHelper;
public Task GetCacheKey(INavigationTreeBuilder builder)
{
if(_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
return Task.FromResult(builder.Name + _currentSite.Id.ToString() + _cultureHelper.CurrentUICultureName());
}
return Task.FromResult(builder.Name + _currentSite.Id.ToString());
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/SiteProjectSettingsResolver.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-07-11
// Last Modified: 2019-03-04
//
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Models;
using cloudscribe.SimpleContent.Web.Services;
using Microsoft.Extensions.Options;
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.Core.SimpleContent.Integration
{
public class SiteProjectSettingsResolver : IProjectSettingsResolver
{
public SiteProjectSettingsResolver(
SiteContext currentSite,
IProjectQueries projectQueries,
IProjectCommands projectCommands,
IOptions uiOptionsAccessor,
CultureHelper cultureHelper
)
{
_currentSite = currentSite;
_projectQueries = projectQueries;
_projectCommands = projectCommands;
_uiOptions = uiOptionsAccessor.Value;
_cultureHelper = cultureHelper;
}
private readonly SiteContext _currentSite;
private readonly IProjectQueries _projectQueries;
private readonly IProjectCommands _projectCommands;
private readonly ContentSettingsUIConfig _uiOptions;
private readonly CultureHelper _cultureHelper;
public async Task GetCurrentProjectSettings(CancellationToken cancellationToken)
{
IProjectSettings settings;
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
var settingsKey = _currentSite.Id.ToString() + "~" + _cultureHelper.CurrentUICultureName();
settings = await _projectQueries.GetProjectSettings(settingsKey, cancellationToken).ConfigureAwait(false);
}
else
{
settings = await _projectQueries.GetProjectSettings(_currentSite.Id.ToString(), cancellationToken).ConfigureAwait(false);
}
//ensure existence of settings
if(settings == null)
{
settings = new ProjectSettings();
settings.Id = _currentSite.Id.ToString();
if(!_uiOptions.ShowBlogMenuOptions)
{
settings.AddBlogToPagesTree = false;
settings.BlogMenuLinksToNewestPost = false;
}
if (_cultureHelper.UseCultureRoutesAndProjects() && !_cultureHelper.IsDefaultCulture())
{
settings.Id = settings.Id + "~" + _cultureHelper.CurrentUICultureName();
}
if (!_uiOptions.ShowBlogMenuOptions)
{
// ShowBlogMenuOptions is true only when "option a" in the vsix template
// which implies that the default page slug should be forced to 'home'.
// Otherwise...
settings.DefaultPageSlug = "page1";
}
await _projectCommands.Create(settings.Id, settings, cancellationToken).ConfigureAwait(false);
}
if (string.IsNullOrEmpty(settings.RecaptchaPublicKey))
{
settings.RecaptchaPublicKey = _currentSite.RecaptchaPublicKey;
settings.RecaptchaPrivateKey = _currentSite.RecaptchaPrivateKey;
}
if (!_uiOptions.ShowBlogMenuOptions)
{
settings.AddBlogToPagesTree = false;
}
settings.TimeZoneId = _currentSite.TimeZoneId;
return settings;
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/SiteRoleSelectorProperties.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-08-26
// Last Modified: 2017-06-09
//
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Web.TagHelpers;
using System.Collections.Generic;
namespace cloudscribe.Core.SimpleContent.Integration
{
public class SiteRoleSelectorProperties : IRoleSelectorProperties
{
public SiteRoleSelectorProperties(SiteContext currentSite)
{
this.currentSite = currentSite;
RequiredScriptPaths = new List();
RequiredScriptPaths.Add("~/cr/js/jquery.unobtrusive-ajax.min.js");
RequiredScriptPaths.Add("~/cr/js/cloudscribe-role-selector.min.js");
}
private SiteContext currentSite;
public string Action
{
get { return "Modal"; }
}
public string Controller
{
get { return "RoleAdmin"; }
}
public Dictionary GetAttributes(string csvTargetElementId, string displayTargetId = "")
{
var result = new Dictionary();
result.Add("data-ajax", "true");
result.Add("data-ajax-begin", "roleSelector.prepareModal('" + csvTargetElementId + "','" + displayTargetId + "')");
result.Add("data-ajax-failure", "roleSelector.clearModal()");
result.Add("data-ajax-method", "GET");
result.Add("data-ajax-mode", "replace");
result.Add("data-ajax-success", "roleSelector.openModal()");
result.Add("data-ajax-update", "#roledialog");
return result;
}
public Dictionary GetRouteParams(string projectId)
{
var result = new Dictionary();
result.Add("siteId", projectId);
return result;
}
public List RequiredScriptPaths { get; private set; }
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/SiteSimpleContentThemeHelper.cs
================================================
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Web.Design;
using Microsoft.Extensions.Options;
namespace cloudscribe.Core.SimpleContent
{
public class SiteSimpleContentThemeHelper : ISimpleContentThemeHelper
{
public SiteSimpleContentThemeHelper(
SiteContext currentSite,
IOptions themeConfigAccessor,
IOptions iconConfigOptionsAccessor
)
{
_currentSite = currentSite;
_themeConfig = themeConfigAccessor.Value;
_iconConfig = iconConfigOptionsAccessor.Value;
}
private SiteContext _currentSite;
private SimpleContentThemeConfig _themeConfig;
private SimpleContentIconConfig _iconConfig;
public SimpleContentThemeSettings GetThemeSettings()
{
SimpleContentThemeSettings result = null;
foreach (var ts in _themeConfig.ThemeSettings)
{
if (ts.ThemeName == _currentSite.Theme)
{
result = ts;
break;
}
}
if(result == null)
{
foreach (var ts in _themeConfig.ThemeSettings)
{
if (ts.ThemeName == "default")
{
result = ts;
break;
}
}
}
if (result == null)
{
result = new SimpleContentThemeSettings();
}
result.Icons = _iconConfig.GetIcons(result.IconSetId);
return result;
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/StartupExtenstions.cs
================================================
using cloudscribe.Core.Models.EventHandlers;
using cloudscribe.Core.SimpleContent;
using cloudscribe.Core.SimpleContent.EventHandlers;
using cloudscribe.Core.SimpleContent.Integration;
using cloudscribe.SimpleContent.Models;
using cloudscribe.SimpleContent.Web.Design;
using cloudscribe.SimpleContent.Web.TagHelpers;
using cloudscribe.Versioning;
using cloudscribe.Web.Navigation.Caching;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection
{
public static class StartupExtenstions
{
public static IServiceCollection AddCloudscribeCoreIntegrationForSimpleContent(
this IServiceCollection services,
IConfiguration configuration = null
)
{
services.AddScoped();
services.AddScoped();
services.TryAddScoped();
services.AddScoped();
services.AddScoped();
services.AddScoped();
services.TryAddScoped();
services.TryAddScoped();
services.AddScoped();
services.AddScoped();
services.AddScoped();
services.AddScoped();
services.AddScoped();
if (configuration != null)
{
services.Configure(configuration.GetSection("ContentSettingsUIConfig"));
}
else
{
services.Configure(c =>
{
// not doing anything just configuring the default
});
}
return services;
}
public static AuthorizationOptions AddCloudscribeCoreSimpleContentIntegrationDefaultPolicies(this AuthorizationOptions options)
{
options.AddPolicy("BlogViewPolicy", policy =>
policy.RequireAssertion(context =>
{
return true; //allow anonymous
})
);
options.AddPolicy(
"BlogEditPolicy",
authBuilder =>
{
//authBuilder.RequireClaim("blogId");
authBuilder.RequireRole("Administrators", "Content Administrators");
}
);
options.AddPolicy(
"PageEditPolicy",
authBuilder =>
{
authBuilder.RequireRole("Administrators", "Content Administrators");
});
options.AddPolicy(
"ViewContentHistoryPolicy",
authBuilder =>
{
authBuilder.RequireRole("Administrators", "Content Administrators");
}
);
return options;
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/VersionProvider.cs
================================================
using cloudscribe.Core.SimpleContent.Integration;
using cloudscribe.Versioning;
using cloudscribe.Web.Common;
using System;
using System.Reflection;
namespace cloudscribe.Core.SimpleContent
{
public class VersionProvider : IVersionProvider
{
private Assembly assembly = typeof(AuthorNameResolver).Assembly;
public string Name
{
get { return assembly.GetName().Name; }
}
public Guid ApplicationId { get { return new Guid("f94177b4-919d-4910-acd1-4b3b1c210ecf"); } }
public Version CurrentVersion
{
get
{
var version = new Version(2, 0, 0, 0);
var versionString = typeof(CloudscribeCommonResources).Assembly.GetCustomAttribute().Version;
if (!string.IsNullOrWhiteSpace(versionString))
{
Version.TryParse(versionString, out version);
}
return version;
}
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/ViewModels/ContentCloningViewModel.cs
================================================
// Copyright (c) Idox Software Ltd All rights reserved.
// Licensed under the Apache License, Version 2.0.
// See License.txt in the project root for license information.
// Author: Simon Annetts, Idox Software Ltd
// Created: 2023-08-17
// Last Modified: 2023-08-17
//
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Models;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace cloudscribe.Core.SimpleContent.Integration.ViewModels
{
public class ContentCloningViewModel
{
public ContentCloningViewModel()
{
}
//this could be passed in from the Site Settings List page
public string SiteId { get; set; } = string.Empty;
public Boolean CloneContentSettings { get; set; } = true;
public Boolean ClonePages { get; set; } = true;
public Boolean RewriteContentUrls { get; set; } = false;
public Boolean CloneBlogPosts { get; set; } = true;
public string CloneFromSiteId { get; set; } = null;
public string CloneFromSiteName { get; set; } = string.Empty;
public int CloneFromPageCount { get; set; } = 0;
public int CloneFromPostCount { get; set; } = 0;
public List CloneFromSites { get; set; } = new List();
public string CloneToSiteId { get; set; } = null;
public string CloneToSiteName { get; set; } = string.Empty;
public int CloneToPageCount { get; set; } = 0;
public int CloneToPostCount { get; set; } = 0;
public List CloneToSites { get; set; } = new List();
public Boolean AllowCloneToSiteSelection { get; set; } = true;
public bool CloneAllowed { get; set; } = false;
public string Command { get; set; } = string.Empty;
public class SiteDetails
{
public string SiteId { get; set; }
public string SiteIdentifier { get; set; }
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/ViewModels/ContentSettingsViewModel.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-08-05
// Last Modified: 2019-02-10
//
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Models;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace cloudscribe.Core.SimpleContent.Integration.ViewModels
{
public class ContentSettingsViewModel
{
public ContentSettingsViewModel()
{
Editors = new List();
}
public List Editors { get; set; }
public string Title { get; set; } = "Blog";
public bool ShowTitle { get; set; } = false;
public string Description { get; set; } = string.Empty;
//public string CopyrightNotice { get; set; } = string.Empty;
//public string Image { get; set; } = string.Empty;
public int PostsPerPage { get; set; } = 5;
public string PubDateFormat { get; set; } = "MMMM d. yyyy";
public bool IncludePubDateInPostUrls { get; set; } = true;
public string LocalMediaVirtualPath { get; set; } = "/media/images/";
public string CdnUrl { get; set; }
public int DaysToComment { get; set; } = -1;
public bool ModerateComments { get; set; } = true;
[EmailAddress(ErrorMessage = "The Notification Email field is not a valid e-mail address.")]
[StringLength(100, ErrorMessage = "Notification Email has a maximum length of 100 characters")]
public string CommentNotificationEmail { get; set; } = string.Empty;
public string DefaultPageSlug { get; set; } = "home";
public string DefaultContentType { get; set; } = "html";
public bool ShowRelatedPosts { get; set; } = true;
public bool ShowBlogCategories { get; set; } = true;
public bool ShowArchivedPosts { get; set; } = true;
public bool ShowAboutBox { get; set; } = true;
public string AboutHeading { get; set; } = "About";
public string AboutContent { get; set; }
// if true automatically add the blog index
public bool AddBlogToPagesTree { get; set; } = true;
public bool BlogMenuLinksToNewestPost { get; set; } = false;
public int BlogPagePosition { get; set; } = 2; // right after home page
[Required(ErrorMessage ="Blog Menu Text is required")]
public string BlogPageText { get; set; } = "Blog";
public string BlogPageNavComponentVisibility { get; set; }
public int DefaultFeedItems { get; set; } = 20;
public int MaxFeedItems { get; set; } = 1000;
[DataType(DataType.Url)]
public string RemoteFeedUrl { get; set; } = string.Empty;
///
/// ie Feedburner User Agent fragment "FeedBurner"
///
public string RemoteFeedProcessorUseAgentFragment { get; set; } = "FeedBurner";
// public bool UseMetaDescriptionInFeed { get; set; } = false;
public int ChannelTimeToLive { get; set; } = 60;
public string LanguageCode { get; set; } = "en-US";
public string ChannelCategoriesCsv { get; set; } = string.Empty;
[EmailAddress(ErrorMessage = "The Notification Email field is not a valid e-mail address.")]
[StringLength(100, ErrorMessage = "Notification Email has a maximum length of 100 characters")]
public string ManagingEditorEmail { get; set; } = string.Empty;
public string ChannelRating { get; set; } = string.Empty;
[EmailAddress(ErrorMessage = "The Webmaster Email field is not a valid e-mail address.")]
[StringLength(100, ErrorMessage = "Webmaster Email has a maximum length of 100 characters")]
public string WebmasterEmail { get; set; } = string.Empty;
public string Publisher { get; set; } = string.Empty;
[DataType(DataType.ImageUrl)]
public string PublisherLogoUrl { get; set; } = string.Empty;
public string PublisherLogoWidth { get; set; } = "500px";
public string PublisherLogoHeight { get; set; } = "500px";
public string PublisherEntityType { get; set; } = "Organization";
public string DisqusShortName { get; set; } = string.Empty;
public bool ShowRecentPostsOnDefaultPage { get; set; }
public bool ShowFeaturedPostsOnDefaultPage { get; set; }
[StringLength(100, ErrorMessage = "FacebookAppId has a maximum length of 100 characters")]
public string FacebookAppId { get; set; }
[StringLength(200, ErrorMessage = "SiteName has a maximum length of 200 characters")]
public string SiteName { get; set; }
[StringLength(100, ErrorMessage = "TwitterPublisher has a maximum length of 100 characters")]
public string TwitterPublisher { get; set; }
[StringLength(100, ErrorMessage = "TwitterCreator has a maximum length of 100 characters")]
public string TwitterCreator { get; set; }
public bool TeasersDisabled { get; set; }
public TeaserMode TeaserMode { get; set; }
public TeaserTruncationMode TeaserTruncationMode { get; set; }
public int TeaserTruncationLength { get; set; } = 20; // Default 20 words.
public bool ShowCreatedBy { get; set; } = false;
public bool ShowCreatedDate { get; set; } = false;
public bool ShowLastModifiedBy { get; set; } = false;
public bool ShowLastModifiedDate { get; set; } = false;
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/ViewModels/CoreCommentNotificationModel.cs
================================================
using cloudscribe.Core.Models;
using cloudscribe.SimpleContent.Models;
using System;
using System.Collections.Generic;
using System.Text;
namespace cloudscribe.Core.SimpleContent.ViewModels
{
public class CoreCommentNotificationModel
{
public CoreCommentNotificationModel(
ISiteContext site,
IProjectSettings project,
IPost post,
IComment comment,
string postUrl)
{
Site = site;
Project = project;
Post = post;
Comment = comment;
PostUrl = postUrl;
}
public ISiteContext Site { get; private set; }
public IProjectSettings Project { get; private set; }
public IPost Post { get; private set; }
public IComment Comment { get; private set; }
public string PostUrl { get; private set; }
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent/cloudscribe.Core.SimpleContent.csproj
================================================
integration library for integrating cloudscribe SimpleContent with cloudscribe Core multi-tenant web app foundation
10.1.0
net10.0
Joe Audette
cloudscribe;blog
icon.png
https://github.com/cloudscribe/cloudscribe.SimpleContent
Apache-2.0
https://github.com/cloudscribe/cloudscribe.SimpleContent.git
git
README.md
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/README.md
================================================
# cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5
[](https://www.nuget.org/packages/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5)
[](https://opensource.org/licenses/Apache-2.0)
Precompiled Bootstrap 5 views for cloudscribe.Core.SimpleContent.
## Installation
```shell
Install-Package cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5
```
## Usage
Add this package to your ASP.NET Core project to use Bootstrap 5 precompiled views with SimpleContent.
## Contributing
Contributions are welcome! Please see the [contributing guidelines](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/CONTRIBUTING.md).
## License
Licensed under the Apache 2.0 License. See the [LICENSE](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/LICENSE) file for details.
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/VersionProvider.cs
================================================
using System;
using System.Reflection;
using cloudscribe.Versioning;
using cloudscribe.Web.Common;
namespace cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5
{
public class VersionProvider : IVersionProvider
{
public string Name { get { return "cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5"; } }
public Guid ApplicationId { get { return new Guid("f93067b4-919d-4910-acd1-4b3b1c210ecf"); } }
public Version CurrentVersion
{
get
{
var version = new Version(2, 0, 0, 0);
var versionString = typeof(CloudscribeCommonResources).Assembly.GetCustomAttribute().Version;
if (!string.IsNullOrWhiteSpace(versionString))
{
Version.TryParse(versionString, out version);
}
return version;
}
}
}
}
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/BlogMetaPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@using cloudscribe.Web.Common.Extensions
@inject ISiteContextResolver siteResolver
@{
var imageUrl = Model.ExtractFirstImageUrl(Model.CurrentPost, Url);
var Tenant = await siteResolver.ResolveSite(Context.Request.Host.Host, Context.Request.Path);
var folderSegment = "";
if(!string.IsNullOrWhiteSpace(Tenant.SiteFolderName))
{
folderSegment = Tenant.SiteFolderName + "/";
}
var rssUrl = string.Format("/{0}api/rss/", folderSegment);
}
@if (Model.CurrentPost != null && !string.IsNullOrEmpty(Model.CurrentPost.MetaDescription))
{
@if (!string.IsNullOrEmpty(imageUrl))
{
}
@if (!string.IsNullOrWhiteSpace(Model.ProjectSettings.SiteName))
{
}
@if (!string.IsNullOrWhiteSpace(Model.ProjectSettings.FacebookAppId))
{
}
@if (!string.IsNullOrWhiteSpace(Model.ProjectSettings.TwitterPublisher))
{
}
@if (!string.IsNullOrWhiteSpace(Model.ProjectSettings.TwitterCreator))
{
}
@if (!string.IsNullOrEmpty(imageUrl))
{
}
}
@*
TODO: implement this stuff
RSD: Discoverability of Blog APIs
http://mashupguide.net/1.0/html/ch05s06.xhtml
http://en.wikipedia.org/wiki/Really_Simple_Discovery
*@
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/ContentCloning/Index.cshtml
================================================
@using cloudscribe.Core.SimpleContent.Integration.ViewModels
@model ContentCloningViewModel
@inject ICoreThemeHelper themeHelper
@inject IStringLocalizer sr
@inject IOptions uiOptionsAccessor
@{
var uiOptions = uiOptionsAccessor.Value;
var themeSettings = themeHelper.GetThemeSettings();
if (themeSettings.AdminSideNavExpanded) { ViewData["SideNavToggle"] = "show"; }
ViewData["SideNavVisible"] = true;
var submitDisabled = Model.CloneAllowed? "" : "disabled";
}
@ViewBag.Title
@section SideNav {
}
@section Toolbar{
}
@section Scripts {
}
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/ContentSettings/CdnUrlPartial.cshtml
================================================
@model cloudscribe.Core.SimpleContent.Integration.ViewModels.ContentSettingsViewModel
@inject IStringLocalizer sr
@* comment this and uncomment the below if you want editable Image Path *@
@*
*@
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/ContentSettings/CommentSystemSettings.cshtml
================================================
@model cloudscribe.Core.SimpleContent.Integration.ViewModels.ContentSettingsViewModel
@inject IStringLocalizer sr
@inject IOptions uiOptionsAccessor
@{
var uiOptions = uiOptionsAccessor.Value;
}
@if (uiOptions.ShowCommentSettings)
{
@if (!uiOptions.ShowBlogSettings)
{
}
@if (uiOptions.ShowBlogSettings)
{
}
}
else
{
}
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/ContentSettings/Index.cshtml
================================================
@model cloudscribe.Core.SimpleContent.Integration.ViewModels.ContentSettingsViewModel
@inject ICoreThemeHelper themeHelper
@inject IStringLocalizer sr
@inject IOptions uiOptionsAccessor
@inject ISummernoteOptionsResolver summernoteOptionsResolver
@inject ICkeditorOptionsResolver editorOptionsResolver
@{
var uiOptions = uiOptionsAccessor.Value;
var themeSettings = themeHelper.GetThemeSettings();
if (themeSettings.AdminSideNavExpanded) { ViewData["SideNavToggle"] = "show"; }
ViewData["SideNavVisible"] = true;
var summernoteOptions = await summernoteOptionsResolver.GetSummernoteOptions();
var ckOptions = await editorOptionsResolver.GetCkeditorOptions();
if (string.IsNullOrWhiteSpace(Model.AboutHeading))
{
Model.AboutHeading = sr["About"];
}
}
@ViewBag.Title
@if (Model.Editors.Count > 0)
{
@sr["Editors"]
@foreach (var user in Model.Editors)
{
-
@user.DisplayName - @user.Email
@sr["Manage"]
}
}
@section Styles {
}
@section SideNav {
}
@section Toolbar{
}
@section Scripts {
}
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/ContentSettings/MediaPathPartial.cshtml
================================================
@model cloudscribe.Core.SimpleContent.Integration.ViewModels.ContentSettingsViewModel
@inject IStringLocalizer sr
@* comment this and uncomment the below if you want editable Image Path *@
@*
*@
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/Page/Tree.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PageTreeViewModel
@inject IStringLocalizer sr
@inject ICoreThemeHelper coreThemeHelper
@inject ISimpleContentThemeHelper themeHelper
@{
var themeSettings = themeHelper.GetThemeSettings();
var coreThemeSettings = coreThemeHelper.GetThemeSettings();
if (coreThemeSettings.AdminSideNavExpanded) { ViewData["SideNavToggle"] = "show"; }
ViewData["SideNavVisible"] = true;
}
@section Styles {
}
@section SideNav {
}
@section Toolbar{
}
@section Scripts {
}
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/Shared/CmsSiteLogoPartial.cshtml
================================================
@model SiteContext
@inject IPageRoutes pageRoutes
@{
var rootUrl = Url.RouteUrl(pageRoutes.PageRouteName, new { siteFolder = Model.SiteFolderName, slug = "" });
}
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/Shared/CmsSiteTitlePartial.cshtml
================================================
@model SiteContext
@inject IPageRoutes pageRoutes
@{
var rootUrl = Url.RouteUrl(pageRoutes.PageRouteName, new { siteFolder = Model.SiteFolderName, slug = "" });
}
@Model.SiteName
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/Shared/CommentNotificationEmail.cshtml
================================================
@model cloudscribe.Core.SimpleContent.ViewModels.CoreCommentNotificationModel
@{
Layout = "_LayoutEmailNotification";
ViewData["Title"] = "Post Comment Notification";
ViewData["Site"] = Model.Site;
}
@Model.Comment.Content
Website: @Model.Comment.Website
E-mail: @Model.Comment.Email
IP-address: @Model.Comment.Ip
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/Views/_ViewImports.cshtml
================================================
@using System
@using System.Collections.Generic
@using System.Globalization
@using System.Linq
@using Microsoft.AspNetCore.Http
@using Microsoft.AspNetCore.Authentication
@using Microsoft.AspNetCore.Identity
@using Microsoft.AspNetCore.Localization
@using Microsoft.Extensions.Localization
@using Microsoft.Extensions.Options
@using cloudscribe.Core.Models
@using cloudscribe.Core.Identity
@using cloudscribe.Core.Web
@using cloudscribe.Core.Web.Components
@using cloudscribe.Core.Web.Design
@using cloudscribe.Web.Navigation
@using cloudscribe.Web.Pagination
@using cloudscribe.Pagination.Models
@using cloudscribe.Web.Common
@using cloudscribe.Web.Common.Components
@using cloudscribe.Web.Common.Helpers
@using cloudscribe.Web.Common.Models
@using cloudscribe.Web.Common.Extensions
@using cloudscribe.SimpleContent.Models
@using cloudscribe.Core.SimpleContent
@using cloudscribe.SimpleContent.Web.TagHelpers
@using cloudscribe.SimpleContent.Web
@using cloudscribe.SimpleContent.Web.Design
@addTagHelper "*, cloudscribe.SimpleContent.Web"
@addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers"
@addTagHelper "*, cloudscribe.Web.Common"
@addTagHelper "*, cloudscribe.Core.Web"
@addTagHelper "*, cloudscribe.Web.Navigation"
@addTagHelper "*, cloudscribe.Web.Pagination"
================================================
FILE: src/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5/cloudscribe.Core.SimpleContent.CompiledViews.Bootstrap5.csproj
================================================
Bootstrap 5 pre-compiled views for cloudscribe Core and SimpleContent integration
10.1.0
net10.0
Joe Audette
true
cloudscribe;SimpleContent;Bootstrap5
icon.png
https://github.com/cloudscribe/cloudscribe.SimpleContent
Apache-2.0
https://github.com/cloudscribe/cloudscribe.SimpleContent.git
git
README.md
1701;1702;0436
1701;1702;0436
================================================
FILE: src/cloudscribe.MetaWeblog/Controllers/MetaWeblogController.cs
================================================
// Licensed under the Apache License, Version 2.0
// Author: Joe Audette
// Created: 2016-02-07
// Last Modified: 2020-02-26
//
using cloudscribe.MetaWeblog.Models;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using System.Xml.Linq;
using Microsoft.AspNetCore.Hosting;
using System.Text;
namespace cloudscribe.MetaWeblog.Controllers
{
[ApiExplorerSettings(IgnoreApi = true)]
public class MetaWeblogController : Controller
{
public MetaWeblogController(
IWebHostEnvironment appEnv,
IMetaWeblogRequestParser metaWeblogRequestParser,
IMetaWeblogRequestProcessor metaWeblogProcessor,
IMetaWeblogResultFormatter metaWeblogResultFormatter,
IMetaWeblogSecurity metaWeblogSecurity,
IMetaWeblogRequestValidator metaWebLogRequestValidator,
ILogger logger,
IOptions optionsAccessor = null)
{
HostingEnvironment = appEnv;
RequestParser = metaWeblogRequestParser;
RequestProcessor = metaWeblogProcessor;
ResultFormatter = metaWeblogResultFormatter;
Security = metaWeblogSecurity;
RequestValidator = metaWebLogRequestValidator;
Log = logger;
if(optionsAccessor != null)
{
ApiOptions = optionsAccessor.Value;
}
else
{
ApiOptions = new ApiOptions(); // just use the default options
}
}
protected IWebHostEnvironment HostingEnvironment { get; private set; }
protected ApiOptions ApiOptions { get; private set; }
protected IMetaWeblogSecurity Security { get; private set; }
protected IMetaWeblogRequestProcessor RequestProcessor { get; private set; }
protected IMetaWeblogRequestParser RequestParser { get; private set; }
protected IMetaWeblogResultFormatter ResultFormatter { get; private set; }
protected IMetaWeblogRequestValidator RequestValidator { get; private set; }
protected ILogger Log { get; private set; }
[HttpPost]
[Route("api/metaweblog")]
public virtual async Task Index()
{
CancellationToken cancellationToken = HttpContext?.RequestAborted ?? CancellationToken.None;
var dumpFileBasePath = HostingEnvironment.ContentRootPath
+ ApiOptions.AppRootDumpFolderVPath.Replace('/', Path.DirectorySeparatorChar);
XDocument postedXml = null;
XDocument resultXml;
MetaWeblogResult outCome;
FaultStruct faultStruct;
try
{
using (HttpContext.Request.Body)
{
string tmp;
using (StreamReader reader = new StreamReader(Request.Body, Encoding.UTF8))
{
tmp = await reader.ReadToEndAsync();
}
postedXml = XDocument.Parse(tmp, LoadOptions.None);
}
}
catch(Exception ex)
{
Log.LogError(ex.Message, ex);
if (ApiOptions.DumpRequestXmlToDisk)
{
var requestFileName = dumpFileBasePath + "request-with-error" + Utils.GetDateTimeStringForFileName(true) + ".txt";
using (StreamWriter s = System.IO.File.CreateText(requestFileName))
{
//postedXml.Save(s, SaveOptions.None);
await HttpContext.Request.Body.CopyToAsync(s.BaseStream);
}
}
outCome = new MetaWeblogResult();
faultStruct = new FaultStruct();
faultStruct.faultCode = "802"; // invalid access
faultStruct.faultString = "invalid access";
outCome.Fault = faultStruct;
resultXml = ResultFormatter.Format(outCome);
return new XmlResult(resultXml);
}
var metaWeblogRequest = RequestParser.ParseRequest(postedXml);
if (ApiOptions.DumpRequestXmlToDisk)
{
var requestFileName = dumpFileBasePath + "request-"
+ Utils.GetDateTimeStringForFileName(true) + "-"
+ metaWeblogRequest.MethodName.Replace(".","-")
+ ".xml";
using (StreamWriter s = System.IO.File.CreateText(requestFileName))
{
postedXml.Save(s, SaveOptions.None);
}
}
var permissions = await Security.ValiatePermissions(metaWeblogRequest, cancellationToken);
if(string.IsNullOrEmpty(metaWeblogRequest.BlogId))
{
metaWeblogRequest.BlogId = permissions.BlogId;
}
if((!permissions.CanEditPosts)&&(!permissions.CanEditPages))
{
outCome = new MetaWeblogResult();
resultXml = ResultFormatter.Format(outCome);
return new XmlResult(resultXml);
}
var isValid = await RequestValidator.IsValid(metaWeblogRequest, cancellationToken);
if (!isValid)
{
outCome = new MetaWeblogResult();
outCome.AddValidatonFault();
resultXml = ResultFormatter.Format(outCome);
return new XmlResult(resultXml);
}
outCome = await RequestProcessor.ProcessRequest(
metaWeblogRequest,
permissions,
cancellationToken);
resultXml = ResultFormatter.Format(outCome);
if (ApiOptions.DumpResponseXmlToDisk)
{
var reseponseFileName = dumpFileBasePath + "response-"
+ Utils.GetDateTimeStringForFileName(true) + "-"
+ outCome.Method.Replace(".", "-")
+ ".xml";
using (StreamWriter s = System.IO.File.CreateText(reseponseFileName))
{
resultXml.Save(s, SaveOptions.None);
}
}
return new XmlResult(resultXml);
}
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/IMetaWeblogRequestParser.cs
================================================
using System.Xml.Linq;
namespace cloudscribe.MetaWeblog
{
public interface IMetaWeblogRequestParser
{
MetaWeblogRequest ParseRequest(XDocument postedDocument);
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/IMetaWeblogRequestProcessor.cs
================================================
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public interface IMetaWeblogRequestProcessor
{
Task ProcessRequest(
MetaWeblogRequest input,
MetaWeblogSecurityResult permission,
CancellationToken cancellationToken);
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/IMetaWeblogRequestValidator.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public interface IMetaWeblogRequestValidator
{
Task IsValid(MetaWeblogRequest request, CancellationToken cancellationToken);
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/IMetaWeblogResultFormatter.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace cloudscribe.MetaWeblog
{
public interface IMetaWeblogResultFormatter
{
XDocument Format(MetaWeblogResult metaWeblogResult);
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/IMetaWeblogSecurity.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-02-15
// Last Modified: 2016-02-15
//
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public interface IMetaWeblogSecurity
{
Task ValiatePermissions(MetaWeblogRequest request, CancellationToken cancellationToken);
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/IMetaWeblogService.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-02-04
// Last Modified: 2016-03-29
//
using cloudscribe.MetaWeblog.Models;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public interface IMetaWeblogService
{
#region MetaWeblog API
//"metaWeblog.newPost
Task NewPost(
string blogId,
string userName,
string password,
PostStruct newPost,
bool publish,
string authorDisplayName
);
//metaWeblog.editPost
Task EditPost(
string blogId,
string postId,
string userName,
string password,
PostStruct post,
bool publish);
//metaWeblog.getPost
Task GetPost(
string blogId,
string postId,
string userName,
string password,
CancellationToken cancellationToken);
//metaWeblog.getCategories
Task> GetCategories(
string blogId,
string userName,
string password,
CancellationToken cancellationToken);
//metaWeblog.getRecentPosts
Task> GetRecentPosts(
string blogId,
string userName,
string password,
int numberOfPosts,
CancellationToken cancellationToken);
//wp.uploadFile
//metaWeblog.newMediaObject
Task NewMediaObject(
string blogId,
string userName,
string password,
MediaObjectStruct mediaObject);
#endregion
#region Blogger API
//blogger.deletePost
//bool DeletePost(string username, string password, string postid);
// void DeletePost(string userName, string password, int postid);
Task DeletePost(
string blogId,
string postId,
string userName,
string password
);
//blogger.getUsersBlogs
//metaWeblog.getUsersBlogs
//wp.getUsersBlogs
//blogger.getUsersBlogs
Task> GetUserBlogs(
string key,
string userName,
string password,
CancellationToken cancellationToken);
#endregion
// wp.newCategory
Task NewCategory(
string blogId,
string category,
string userName,
string password
);
//wp.getPages method
Task> GetPages(
string blogId,
string userName,
string password,
CancellationToken cancellationToken);
//wp.getPageList method
Task> GetPageList(
string blogId,
string userName,
string password,
CancellationToken cancellationToken);
//wp.getPage method
Task GetPage(
string blogId,
string pageId,
string userName,
string password,
CancellationToken cancellationToken);
//wp.newPage method
Task NewPage(
string blogId,
string userName,
string password,
PageStruct newPage,
bool publish);
//wp.editPage
Task EditPage(
string blogId,
string pageId,
string userName,
string password,
PageStruct page,
bool publish);
//wp.deletePage
Task DeletePage(
string blogId,
string pageId,
string userName,
string password
);
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/MetaWeblogRequest.cs
================================================
using cloudscribe.MetaWeblog.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public class MetaWeblogRequest
{
public MetaWeblogRequest()
{
}
///
/// Gets AppKey is a key generated by the calling application. It is sent with blogger API calls.
///
///
/// BlogEngine.NET doesn't require specific AppKeys for API calls. It is no longer standard practive.
///
public string AppKey { get; set; }
///
/// Gets ID of the Blog to call the function on. Since BlogEngine supports only a single blog instance,
/// this incoming parameter is not used.
///
public string BlogId { get; set; }
///
/// Gets MediaObject is a struct sent by the metaWeblog.newMediaObject function.
/// It contains information about the media and the object in a bit array.
///
public MediaObjectStruct MediaObject { get; set; }
///
/// Gets Name of Called Metaweblog Function
///
public string MethodName { get; set; }
///
/// Gets Number of post request by the metaWeblog.getRecentPosts function
///
public int NumberOfPosts { get; set; }
///
/// Gets Metaweblog Page Struct
///
public PageStruct Page { get; set; }
///
/// Gets PageID Guid in string format
///
public string PageId { get; set; }
///
/// Gets Password for user validation
///
public string Password { get; set; }
///
/// Gets Metaweblog Post struct containing information post including title, content, and categories.
///
public PostStruct Post { get; set; }
///
/// Gets The PostID Guid in string format
///
public string PostId { get; set; }
///
/// Gets a value indicating whether or not a post will be marked as published by BlogEngine.
///
public bool Publish { get; set; }
///
/// Gets Login for user validation
///
public string UserName { get; set; }
///
/// Gets Category for wp.newCategory
///
public string Category { get; set; }
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/MetaWeblogRequestParser.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-02-04
// Last Modified: 2016-02-17
//
using cloudscribe.MetaWeblog.Models;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Xml.Linq;
namespace cloudscribe.MetaWeblog
{
public class MetaWeblogRequestParser : IMetaWeblogRequestParser
{
private ILogger log = null;
private XDocument postedXml;
private List inputParams = null;
public MetaWeblogRequestParser(ILogger logger = null)
{
log = logger;
}
public MetaWeblogRequest ParseRequest(XDocument postedDocument)
{
//postedXml = XDocument.Load(context.Request.Body, LoadOptions.None);
postedXml = postedDocument;
MetaWeblogRequest result = new MetaWeblogRequest();
var methodCallElement = postedXml.Document.Element("methodCall");
//if (rootElement == null) throw new HttpException(400, @"The ""methodCall"" element is missing from the XML-RPC request body.");
var methodNameElement = methodCallElement.Element("methodName");
result.MethodName = methodNameElement.Value;
var paramsElement = methodCallElement.Elements("params");
inputParams = paramsElement.Descendants("param").ToList();
//inputParams.
// Determine what params are what by method name
switch (result.MethodName)
{
case "system.listMethods":
case "wpcom.getFeatures":
//do nothing
break;
case "metaWeblog.newPost":
result.BlogId = inputParams[0].Descendants("string").SingleOrDefault().Value;
result.UserName = inputParams[1].Descendants("string").SingleOrDefault().Value;
result.Password = inputParams[2].Descendants("string").SingleOrDefault().Value;
result.Post = GetPost(inputParams[3]);
if (inputParams.Count > 4)
{
var p = inputParams[4].Descendants("boolean").SingleOrDefault().Value;
result.Publish = p != "0" && p != "false";
}
else
{
result.Publish = GetIsPublished(inputParams[3]);
}
break;
case "metaWeblog.editPost":
result.PostId = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
result.Post = GetPost(inputParams[3]);
//this.Publish = this.inputParams[4].InnerText != "0" && this.inputParams[4].InnerText != "false";
if (inputParams.Count > 4)
{
var p = inputParams[4].Descendants("boolean").SingleOrDefault().Value;
result.Publish = p != "0" && p != "false";
}
else
{
result.Publish = GetIsPublished(inputParams[3]);
}
break;
case "metaWeblog.getPost":
result.PostId = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
break;
case "metaWeblog.newMediaObject":
case "wp.uploadFile":
result.BlogId = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
result.MediaObject = GetMediaObject(inputParams[3]);
break;
case "metaWeblog.getCategories":
case "wp.getCategories":
case "wp.getAuthors":
case "wp.getPageList":
case "wp.getPages":
case "wp.getTags":
result.BlogId = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
break;
case "wp.newCategory":
result.BlogId = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
result.Category = GetCategory(inputParams[3]);
break;
case "metaWeblog.getRecentPosts":
result.BlogId = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
result.NumberOfPosts = Int32.Parse(inputParams[3].Value, CultureInfo.InvariantCulture);
break;
case "blogger.getUsersBlogs":
case "metaWeblog.getUsersBlogs":
result.AppKey = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
break;
case "wp.getUsersBlogs":
result.UserName = inputParams[0].Value;
result.Password = inputParams[1].Value;
break;
case "blogger.deletePost":
result.AppKey = inputParams[0].Descendants("string").SingleOrDefault().Value;
result.PostId = inputParams[1].Descendants("string").SingleOrDefault().Value;
result.UserName = inputParams[2].Descendants("string").SingleOrDefault().Value;
result.Password = inputParams[3].Descendants("string").SingleOrDefault().Value;
result.Publish = inputParams[4].Descendants("boolean").SingleOrDefault().Value != "0"
&& inputParams[4].Descendants("boolean").SingleOrDefault().Value != "false";
break;
case "blogger.getUserInfo":
result.AppKey = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
break;
case "wp.newPage":
result.BlogId = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
result.Page = GetPage(inputParams[3]);
if (inputParams.Count > 4)
{
var p = inputParams[4].Descendants("boolean").SingleOrDefault().Value;
result.Publish = p != "0" && p != "false";
//result.Publish = inputParams[4].Value != "0" && inputParams[4].Value != "false";
}
break;
case "wp.getPage":
result.BlogId = inputParams[0].Value;
result.PageId = inputParams[1].Value;
result.UserName = inputParams[2].Value;
result.Password = inputParams[3].Value;
break;
case "wp.editPage":
result.BlogId = inputParams[0].Value;
result.PageId = inputParams[1].Value;
result.UserName = inputParams[2].Value;
result.Password = inputParams[3].Value;
result.Page = GetPage(inputParams[4]);
if (inputParams.Count > 5)
{
var p = inputParams[5].Descendants("boolean").SingleOrDefault().Value;
result.Publish = p != "0" && p != "false";
}
else
{
result.Publish = GetPublish(inputParams[4]);
}
//result. = inputParams[5].Value;
break;
case "wp.deletePage":
result.BlogId = inputParams[0].Value;
result.UserName = inputParams[1].Value;
result.Password = inputParams[2].Value;
result.PageId = inputParams[3].Value;
break;
default:
throw new MetaWeblogException("02", string.Format("Unknown Method. ({0})", result.MethodName));
}
return result;
}
private static bool GetIsPublished(XElement paramNode)
{
//XmlNode status = node.SelectSingleNode("value/struct/member[name='post_status']");
//var status = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "post_status");
var statusMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "post_status"
);
if ((statusMember != null) && ((statusMember.LastNode as XElement).Value == "publish")) { return true; }
return false;
}
private static bool GetPublish(XElement paramNode)
{
//var statusNode = node.SelectSingleNode("value/struct/member[name='page_status']");
//var statusNode = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "page_status");
var statusMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "page_status"
);
if (statusMember == null)
{
throw new MetaWeblogException("06", "Page Struct Element, page_status, not Sent.");
}
string result = (statusMember.LastNode as XElement).Value;
if (result == "publish") { return true; }
if (result == "true") { return true; }
if (result == "1") { return true; }
return false;
}
private static string GetCategory(XElement paramNode)
{
//var categoryNode = node.SelectSingleNode("value/struct/member[name='name']");
//var categoryNode = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "name");
var categoryMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "name"
);
if (categoryMember == null)
{
throw new MetaWeblogException("06", "Category Struct Element, name, not Sent.");
}
return (categoryMember.LastNode as XElement).Value;
}
///
/// Creates a Metaweblog Post object from the XML struct
///
///
/// XML contains a Metaweblog Post Struct
///
///
/// Metaweblog Post Struct Obejct
///
private static PostStruct GetPost(XElement paramNode)
{
var temp = new PostStruct();
// Require Title and Description
//var title = node.SelectSingleNode("value/struct/member[name='title']");
var titleMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "title"
);
if (titleMember == null)
{
throw new MetaWeblogException("05", "Page Struct Element, Title, not Sent.");
}
//temp.title = title.LastChild.InnerText;
temp.title = titleMember.Descendants("string").FirstOrDefault().Value;
//var description = node.SelectSingleNode("value/struct/member[name='description']");
var descriptionMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "description"
);
if (descriptionMember == null)
{
throw new MetaWeblogException("05", "Page Struct Element, Description, not Sent.");
}
//temp.description = description.LastChild.InnerText;
temp.description = descriptionMember.Descendants("string").FirstOrDefault().Value;
//var link = node.SelectSingleNode("value/struct/member[name='link']");
var linkMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "link"
);
//temp.link = link == null ? string.Empty : link.LastChild.InnerText;
temp.link = linkMember == null ? string.Empty : linkMember.Descendants("string").FirstOrDefault().Value;
//var allowComments = node.SelectSingleNode("value/struct/member[name='mt_allow_comments']");
//temp.commentPolicy = allowComments == null ? string.Empty : allowComments.LastChild.InnerText;
var allowCommentsMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "mt_allow_comments"
);
//temp.commentPolicy = allowComments == null ? string.Empty : (allowComments.LastNode as XElement).Value;
temp.commentPolicy = allowCommentsMember == null ? string.Empty : (allowCommentsMember.LastNode as XElement).Value;
//var excerpt = node.SelectSingleNode("value/struct/member[name='mt_excerpt']");
//temp.excerpt = excerpt == null ? string.Empty : excerpt.LastChild.InnerText;
var excerptMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "mt_excerpt"
);
temp.excerpt = excerptMember == null ? string.Empty : (excerptMember.LastNode as XElement).Value;
//var slug = node.SelectSingleNode("value/struct/member[name='wp_slug']");
//temp.slug = slug == null ? string.Empty : slug.LastChild.InnerText;
//var slug = paramNode.Descendants("member")
// .First(i => (string)i.Attribute("name") == "wp_slug");
var slugMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "wp_slug"
);
temp.slug = slugMember == null ? string.Empty : (slugMember.LastNode as XElement).Value;
//var authorId = node.SelectSingleNode("value/struct/member[name='wp_author_id']");
//temp.author = authorId == null ? string.Empty : authorId.LastChild.InnerText;
var authorMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "wp_author_id"
);
temp.author = authorMember == null ? string.Empty : (authorMember.LastNode as XElement).Value;
var cats = new List();
//var categories = node.SelectSingleNode("value/struct/member[name='categories']");
var categoriesMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "categories"
);
if (categoriesMember != null)
{
var categoryArray = categoriesMember.LastNode as XElement;
//var categoryArrayNodes = categoryArray.SelectNodes("array/data/value/string");
var categoryArrayNodes = categoryArray.Descendants("string");
if (categoryArrayNodes != null)
{
cats.AddRange(categoryArrayNodes.Cast().Select(
catnode => catnode.Value));
}
}
temp.categories = cats;
// postDate has a few different names to worry about
//var dateCreated = node.SelectSingleNode("value/struct/member[name='dateCreated']");
var dateCreatedMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "dateCreated"
);
//var pubDate = node.SelectSingleNode("value/struct/member[name='pubDate']");
var pubDateMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "pubDate"
);
if (dateCreatedMember != null)
{
try
{
var tempDate = (dateCreatedMember.LastNode as XElement).Value;
temp.postDate = DateTime.ParseExact(
tempDate,
"yyyyMMdd'T'HH':'mm':'ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal);
}
catch (Exception ex)
{
// Ignore PubDate Error
Debug.WriteLine(ex.Message);
}
}
else if (pubDateMember != null)
{
try
{
var tempPubDate = (pubDateMember.LastNode as XElement).Value;
temp.postDate = DateTime.ParseExact(
tempPubDate,
"yyyyMMdd'T'HH':'mm':'ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal);
}
catch (Exception ex)
{
// Ignore PubDate Error
Debug.WriteLine(ex.Message);
}
}
// WLW tags implementation using mt_keywords
var tags = new List();
var keyWordsMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "mt_keywords"
);
if (keyWordsMember != null)
{
var tagsList = (keyWordsMember.LastNode as XElement).Value;
foreach (var item in
tagsList.Split(',').Where(item => string.IsNullOrEmpty(tags.Find(t => t.Equals(item.Trim(), StringComparison.OrdinalIgnoreCase)))))
{
tags.Add(item.Trim());
}
}
temp.tags = tags;
return temp;
}
private PageStruct GetPage(XElement paramNode)
{
var temp = new PageStruct();
// Require Title and Description
//var title = node.SelectSingleNode("value/struct/member[name='title']");
//var title = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "title");
var titleMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "title"
);
if (titleMember == null)
{
throw new MetaWeblogException("06", "Page Struct Element, Title, not Sent.");
}
temp.title = (titleMember.LastNode as XElement).Value;
//var description = node.SelectSingleNode("value/struct/member[name='description']");
//var description = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "description");
var descMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "description"
);
if (descMember == null)
{
throw new MetaWeblogException("06", "Page Struct Element, Description, not Sent.");
}
temp.description = (descMember.LastNode as XElement).Value;
//var link = node.SelectSingleNode("value/struct/member[name='link']");
//if (link != null)
//{
// temp.link = node.SelectSingleNode("value/struct/member[name='link']") == null ? null : link.LastChild.InnerText;
//}
//var link = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "link");
var linkMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "link"
);
temp.link = linkMember == null ? string.Empty : (linkMember.LastNode as XElement).Value;
var slugMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "wp_slug"
);
temp.slug = slugMember == null ? string.Empty : (slugMember.LastNode as XElement).Value;
//var dateCreated = node.SelectSingleNode("value/struct/member[name='dateCreated']");
//var dateCreated = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "dateCreated");
var dateCreatedMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "dateCreated"
);
if (dateCreatedMember != null)
{
try
{
var tempDate = (dateCreatedMember.LastNode as XElement).Value;
temp.pageDate = DateTime.ParseExact(
tempDate,
"yyyyMMdd'T'HH':'mm':'ss",
CultureInfo.InvariantCulture,
DateTimeStyles.AssumeUniversal);
}
catch (Exception ex)
{
// Ignore PubDate Error
if (log != null)
{
log.LogError("swallowed pub date error", ex);
}
}
}
// Keywords
//var keywords = node.SelectSingleNode("value/struct/member[name='mt_keywords']");
//temp.mt_keywords = keywords == null ? string.Empty : keywords.LastChild.InnerText;
//var keywords = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "mt_keywords");
var keywordsMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "mt_keywords"
);
temp.mt_keywords = keywordsMember == null ? string.Empty : (keywordsMember.LastNode as XElement).Value;
//var pageParentId = node.SelectSingleNode("value/struct/member[name='wp_page_parent_id']");
//temp.pageParentID = pageParentId == null ? null : pageParentId.LastChild.InnerText;
//var pageParentId = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "wp_page_parent_id");
var parentIdMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "wp_page_parent_id"
);
temp.pageParentId = parentIdMember == null ? null : (parentIdMember.LastNode as XElement).Value;
//var pageOrder = node.SelectSingleNode("value/struct/member[name='wp_page_order']");
//temp.pageOrder = pageOrder == null ? null : pageOrder.LastChild.InnerText;
//var pageOrder = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "wp_page_order");
var pageOrderMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "wp_page_order"
);
temp.pageOrder = pageOrderMember == null ? null : (pageOrderMember.LastNode as XElement).Value;
//var allowComments = node.SelectSingleNode("value/struct/member[name='mt_allow_comments']");
//temp.commentPolicy = allowComments == null ? string.Empty : allowComments.LastChild.InnerText;
//var allowComments = node.Descendants("member")
// .First(i => (string)i.Attribute("name") == "mt_allow_comments");
var allowCommentsMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "mt_allow_comments"
);
temp.commentPolicy = allowCommentsMember == null ? string.Empty : (allowCommentsMember.LastNode as XElement).Value;
return temp;
}
private static MediaObjectStruct GetMediaObject(XElement paramNode)
{
//var name = node.SelectSingleNode("value/struct/member[name='name']");
//var type = node.SelectSingleNode("value/struct/member[name='type']");
//var bits = node.SelectSingleNode("value/struct/member[name='bits']");
var nameMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "name"
);
var typeMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "type"
);
var bitsMember = paramNode.Descendants("member")
.FirstOrDefault(p =>
p.Element("name").Value == "bits"
);
var temp = new MediaObjectStruct
{
name = nameMember == null ? string.Empty : nameMember.Descendants("string").FirstOrDefault().Value,
type = typeMember == null ? "notsent" : typeMember.Descendants("string").FirstOrDefault().Value,
bytes = Convert.FromBase64String(bitsMember == null ? string.Empty : bitsMember.Descendants("base64").FirstOrDefault().Value)
};
return temp;
}
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/MetaWeblogRequestProcessor.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-02-04
// Last Modified: 2016-08-10
//
using cloudscribe.MetaWeblog.Models;
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public class MetaWeblogRequestProcessor : IMetaWeblogRequestProcessor
{
public MetaWeblogRequestProcessor(
IMetaWeblogService metaWeblogService)
{
service = metaWeblogService;
}
private IMetaWeblogService service;
public async Task ProcessRequest(
MetaWeblogRequest input,
MetaWeblogSecurityResult permission,
CancellationToken cancellationToken)
{
MetaWeblogResult output = new MetaWeblogResult();
output.Method = input.MethodName;
switch (input.MethodName)
{
case "metaWeblog.newPost":
if(permission.CanEditPosts)
{
output.PostId = await service.NewPost(
input.BlogId,
input.UserName,
input.Password,
input.Post,
input.Publish,
permission.DisplayName
);
}
else
{
output.AddSecurityFault();
}
break;
case "metaWeblog.editPost":
if (permission.CanEditPosts)
{
output.Completed = await service.EditPost(
input.BlogId,
input.PostId,
input.UserName,
input.Password,
input.Post,
input.Publish);
}
else
{
output.AddSecurityFault();
}
break;
case "metaWeblog.getPost":
if (permission.CanEditPosts)
{
var postStruct = await service.GetPost(
input.BlogId,
input.PostId,
input.UserName,
input.Password,
cancellationToken
);
output.Post = postStruct;
if (string.IsNullOrEmpty(output.Post.postId))
{
output.Fault = new FaultStruct { faultCode = "404", faultString = "post not found" };
}
}
else
{
output.AddSecurityFault();
}
break;
case "metaWeblog.newMediaObject":
case "wp.uploadFile":
if (permission.CanEditPosts || permission.CanEditPages)
{
output.MediaInfo = await service.NewMediaObject(
input.BlogId,
input.UserName,
input.Password,
input.MediaObject);
}
else
{
output.AddSecurityFault();
}
break;
case "metaWeblog.getCategories":
if (permission.CanEditPosts || permission.CanEditPages)
{
output.Categories = await service.GetCategories(
input.BlogId,
input.UserName,
input.Password,
cancellationToken);
}
else
{
output.AddSecurityFault();
}
break;
case "wp.newCategory":
if (permission.CanEditPosts || permission.CanEditPages)
{
output.CategoryId = await service.NewCategory(
input.BlogId,
input.Category,
input.UserName,
input.Password
);
}
else
{
output.AddSecurityFault();
}
break;
case "metaWeblog.getRecentPosts":
if (permission.CanEditPosts)
{
var posts = await service.GetRecentPosts(
input.BlogId,
input.UserName,
input.Password,
input.NumberOfPosts,
cancellationToken);
output.Posts = posts;
}
else
{
output.AddSecurityFault();
}
break;
case "blogger.getUsersBlogs":
case "metaWeblog.getUsersBlogs":
case "wp.getUsersBlogs":
output.Blogs = await service.GetUserBlogs(
input.AppKey,
input.UserName,
input.Password,
cancellationToken);
break;
case "blogger.deletePost":
if (permission.CanEditPosts)
{
output.Completed = await service.DeletePost(
input.BlogId,
input.PostId,
input.UserName,
input.Password
);
}
else
{
output.AddSecurityFault();
}
break;
case "blogger.getUserInfo":
// Not implemented. Not planned.
throw new MetaWeblogException("10", "The method GetUserInfo is not implemented.");
case "wp.newPage":
if (permission.CanEditPages)
{
output.PageId = await service.NewPage(
input.BlogId,
input.UserName,
input.Password,
input.Page,
input.Publish);
}
else
{
output.AddSecurityFault();
}
break;
case "wp.getPageList":
if (permission.CanEditPages)
{
output.Pages = await service.GetPageList(
input.BlogId,
input.UserName,
input.Password,
cancellationToken);
}
else
{
output.AddSecurityFault();
}
break;
case "wp.getPages":
if (permission.CanEditPages)
{
output.Pages = await service.GetPages(
input.BlogId,
input.UserName,
input.Password,
cancellationToken);
}
else
{
output.AddSecurityFault();
}
break;
case "wp.getPage":
if (permission.CanEditPages)
{
output.Page = await service.GetPage(
input.BlogId,
input.PageId,
input.UserName,
input.Password,
cancellationToken);
}
else
{
output.AddSecurityFault();
}
break;
case "wp.editPage":
if (permission.CanEditPages)
{
output.Completed = await service.EditPage(
input.BlogId,
input.PageId,
input.UserName,
input.Password,
input.Page,
input.Publish);
}
else
{
output.AddSecurityFault();
}
break;
case "wp.deletePage":
if (permission.CanEditPages)
{
output.Completed = await service.DeletePage(
input.BlogId,
input.PageId,
input.UserName,
input.Password
);
}
else
{
output.AddSecurityFault();
}
break;
case "wp.getAuthors":
// Not implemented.
throw new MetaWeblogException("10", "The method getAuthors is not implemented.");
//output.Authors = this.GetAuthors(input.BlogID, input.UserName, input.Password);
//break;
case "wp.getTags":
// Not implemented.
throw new MetaWeblogException("10", "The method getTags is not implemented.");
//output.Keywords = this.GetKeywords(input.BlogID, input.UserName, input.Password);
//break;
}
return output;
}
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/MetaWeblogRequestValidator.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-02-06
// Last Modified: 2016-02-15
//
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public class MetaWeblogRequestValidator : IMetaWeblogRequestValidator
{
public Task IsValid(MetaWeblogRequest request, CancellationToken cancellationToken)
{
if(string.IsNullOrEmpty(request.MethodName)) { return Task.FromResult(false); }
switch (request.MethodName)
{
case "system.listMethods":
return Task.FromResult(true);
case "wpcom.getFeatures":
return Task.FromResult(true);
case "metaWeblog.newPost":
if(string.IsNullOrEmpty(request.BlogId)) { return Task.FromResult(false); }
if(string.IsNullOrEmpty(request.Post.title)) { return Task.FromResult(false); }
return Task.FromResult(true);
case "metaWeblog.getPost":
//TODO: more checks
return Task.FromResult(true);
case "metaWeblog.newMediaObject":
case "wp.uploadFile":
var requestedFileExtension = Path.GetExtension(request.MediaObject.name);
//TODO: validate extension against white list of allowed extensions
return Task.FromResult(true);
case "metaWeblog.getCategories":
case "wp.getCategories":
return Task.FromResult(true);
case "wp.newCategory":
return Task.FromResult(true);
case "metaWeblog.getRecentPosts":
return Task.FromResult(true);
case "blogger.getUsersBlogs":
case "metaWeblog.getUsersBlogs":
return Task.FromResult(true);
case "wp.getUsersBlogs":
return Task.FromResult(true);
case "metaWeblog.editPost":
case "blogger.deletePost":
case "wp.editPage":
case "wp.deletePage":
return Task.FromResult(true);
case "wp.newPage":
return Task.FromResult(true);
case "wp.getPage":
return Task.FromResult(true);
case "wp.getPageList":
return Task.FromResult(true);
case "wp.getPages":
return Task.FromResult(true);
case "wp.getAuthors":
return Task.FromResult(true);
case "wp.getTags":
return Task.FromResult(true);
}
return Task.FromResult(false);
}
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/MetaWeblogResult.cs
================================================
using cloudscribe.MetaWeblog.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public class MetaWeblogResult
{
public MetaWeblogResult()
{
Blogs = new List();
Categories = new List();
Keywords = new List();
Posts = new List();
Pages = new List();
Authors = new List();
}
public string Method { get; set; }
public List Authors { get; set; }
public List Blogs { get; set; }
public List Categories { get; set; }
///
/// Gets or sets a value indicating whether function call was completed and successful.
/// Used by metaWeblog.editPost and blogger.deletePost.
///
public bool Completed { get; set; }
///
/// Gets or sets Fault Struct. Used by API to return error information
///
public FaultStruct Fault { get; set; }
///
/// Gets or sets List of Tags. Used by wp.getTags.
///
public List Keywords { get; set; }
public MediaInfoStruct MediaInfo { get; set; }
public PageStruct Page { get; set; }
///
/// Gets or sets Id of page that was just added.
///
public string PageId { get; set; }
///
/// Gets or sets List of Page Structs
///
public List Pages { get; set; }
///
/// Gets or sets Metaweblog Post Struct. Used by metaWeblog.getPost
///
public PostStruct Post { get; set; }
///
/// Gets or sets Id of post that was just added. Used by metaWeblog.newPost
///
public string PostId { get; set; }
///
/// Gets or sets List of Metaweblog Post Structs. Used by metaWeblog.getRecentPosts
///
public List Posts { get; set; }
///
/// Gets or sets Id of Category that was just added. Used by wp.newCategory
///
public string CategoryId { get; set; }
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/MetaWeblogResultExtensions.cs
================================================
using cloudscribe.MetaWeblog.Models;
namespace cloudscribe.MetaWeblog
{
public static class MetaWeblogResultExtensions
{
public static MetaWeblogResult AddSecurityFault(this MetaWeblogResult result)
{
var faultStruct = new FaultStruct();
faultStruct.faultCode = "11"; // invalid access
faultStruct.faultString = "Authentication Failed";
result.Fault = faultStruct;
return result;
}
public static MetaWeblogResult AddValidatonFault(this MetaWeblogResult result)
{
var faultStruct = new FaultStruct();
faultStruct.faultCode = "802"; // invalid access
faultStruct.faultString = "invalid request";
result.Fault = faultStruct;
return result;
}
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/MetaWeblogResultFormatter.cs
================================================
// Copyright (c) Source Tree Solutions, LLC. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
// Author: Joe Audette
// Created: 2016-02-04
// Last Modified: 2016-02-17
//
using System.Xml.Linq;
namespace cloudscribe.MetaWeblog
{
public class MetaWeblogResultFormatter : IMetaWeblogResultFormatter
{
public XDocument Format(MetaWeblogResult metaWeblogResult)
{
var xml = new XDocument();
var methodResponse = new XElement("methodResponse");
xml.Add(methodResponse);
if (!string.IsNullOrEmpty(metaWeblogResult.Fault.faultCode))
{
BuildFaultResponse(methodResponse, metaWeblogResult);
return xml;
}
var methodParams = new XElement("params");
methodResponse.Add(methodParams);
switch(metaWeblogResult.Method)
{
case "system.listMethods":
BuildListMethodsResponse(methodParams, metaWeblogResult);
break;
case "wpcom.getFeatures":
BuildWPGetFeaturesResponse(methodParams, metaWeblogResult);
break;
case "metaWeblog.newPost":
BuildNewPostResponse(methodParams, metaWeblogResult);
break;
case "metaWeblog.getPost":
BuildGetPostResponse(methodParams, metaWeblogResult);
break;
case "metaWeblog.newMediaObject":
case "wp.uploadFile":
BuildMediaInfoResponse(methodParams, metaWeblogResult);
break;
case "metaWeblog.getCategories":
case "wp.getCategories":
BuildGetCategoriesResponse(methodParams, metaWeblogResult);
break;
case "wp.newCategory":
BuildNewCategoryResponse(methodParams, metaWeblogResult);
break;
case "metaWeblog.getRecentPosts":
BuildRecentPostsResponse(methodParams, metaWeblogResult);
break;
case "blogger.getUsersBlogs":
case "metaWeblog.getUsersBlogs":
BuildUserBlogsResponse(methodParams, metaWeblogResult);
break;
case "wp.getUsersBlogs":
BuildWPUserBlogsResponse(methodParams, metaWeblogResult);
break;
case "metaWeblog.editPost":
case "blogger.deletePost":
case "wp.editPage":
case "wp.deletePage":
BuildActionResultBoolResponse(methodParams, metaWeblogResult);
break;
case "wp.newPage":
BuildNewPageResponse(methodParams, metaWeblogResult);
break;
case "wp.getPage":
BuildGetPageResponse(methodParams, metaWeblogResult);
break;
case "wp.getPageList":
BuildWPPageListResponse(methodParams, metaWeblogResult);
break;
case "wp.getPages":
BuildWPGetPagesResponse(methodParams, metaWeblogResult);
break;
case "wp.getAuthors":
BuildGetAuthorsResponse(methodParams, metaWeblogResult);
break;
case "wp.getTags":
BuildGetTagsResponse(methodParams, metaWeblogResult);
break;
}
return xml;
}
private void BuildGetTagsResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
methodParams.Add(paramElement);
var valueElement = new XElement("value");
var arrayElement = new XElement("array");
var dataElement = new XElement("data");
paramElement.Add(valueElement);
valueElement.Add(arrayElement);
arrayElement.Add(dataElement);
foreach (var keyword in metaWeblogResult.Keywords)
{
var v = new XElement("value");
dataElement.Add(v);
var structElement = new XElement("struct");
v.Add(structElement);
var IdElement
= new XElement("member",
new XElement("name", "name"),
new XElement("value",
new XElement("string", keyword)
) // end value
); // end member
structElement.Add(IdElement);
}
}
private void BuildGetAuthorsResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
methodParams.Add(paramElement);
var valueElement = new XElement("value");
var arrayElement = new XElement("array");
var dataElement = new XElement("data");
paramElement.Add(valueElement);
valueElement.Add(arrayElement);
arrayElement.Add(dataElement);
foreach (var author in metaWeblogResult.Authors)
{
var v = new XElement("value");
dataElement.Add(v);
var structElement = new XElement("struct");
v.Add(structElement);
var IdElement
= new XElement("member",
new XElement("name", "user_id"),
new XElement("value",
new XElement("string", author.user_id)
) // end value
); // end member
structElement.Add(IdElement);
var lohginElement
= new XElement("member",
new XElement("name", "user_login"),
new XElement("value",
new XElement("string", author.user_login)
) //end value
); // end member
structElement.Add(lohginElement);
var emailElement
= new XElement("member",
new XElement("name", "user_email"),
new XElement("value",
new XElement("string", author.user_email)
) //end value
); // end member
structElement.Add(emailElement);
var metaElement
= new XElement("member",
new XElement("name", "meta_value"),
new XElement("value",
new XElement("string", author.meta_value)
) //end value
); // end member
structElement.Add(metaElement);
}
}
private void BuildWPGetPagesResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
methodParams.Add(paramElement);
var valueElement = new XElement("value");
var arrayElement = new XElement("array");
var dataElement = new XElement("data");
paramElement.Add(valueElement);
valueElement.Add(arrayElement);
arrayElement.Add(dataElement);
foreach (var page in metaWeblogResult.Pages)
{
var v = new XElement("value");
dataElement.Add(v);
var structElement = new XElement("struct");
v.Add(structElement);
var blogIdElement
= new XElement("member",
new XElement("name", "page_id"),
new XElement("value",
new XElement("string", page.pageId)
) // end value
); // end member
structElement.Add(blogIdElement);
var pageNameElement
= new XElement("member",
new XElement("name", "page_title"),
new XElement("value",
new XElement("string", page.title)
) //end value
); // end member
structElement.Add(pageNameElement);
var pageTitleElement
= new XElement("member",
new XElement("name", "title"),
new XElement("value",
new XElement("string", page.title)
) //end value
); // end member
structElement.Add(pageTitleElement);
var descElement
= new XElement("member",
new XElement("name", "description"),
new XElement("value",
new XElement("string", page.description)
) //end value
); // end member
structElement.Add(descElement);
var linkElement
= new XElement("member",
new XElement("name", "link"),
new XElement("value",
new XElement("string", page.link)
) //end value
); // end member
structElement.Add(linkElement);
var breaksMember =
new XElement("member",
new XElement("name", "mt_convert_breaks"),
new XElement("value",
new XElement("string", "__default__")
) // end value
); //end member
structElement.Add(breaksMember);
if (!string.IsNullOrEmpty(page.pageParentId))
{
var ppiMember =
new XElement("member",
new XElement("name", "wp_page_parent_id"),
new XElement("value",
new XElement("string", page.pageParentId)
) // end value
); //end member
structElement.Add(ppiMember);
}
//var parentTitleElement
// = new XElement("member",
// new XElement("name", "wp_page_parent_title"),
// new XElement("value",
// new XElement("string", page.parentTitle)
// ) //end value
// ); // end member
//structElement.Add(parentTitleElement);
if (!string.IsNullOrEmpty(page.pageOrder))
{
var commentPolicyMember =
new XElement("member",
new XElement("name", "wp_page_order"),
new XElement("value",
new XElement("string", page.pageOrder)
) // end value
); //end member
structElement.Add(commentPolicyMember);
}
if (!string.IsNullOrEmpty(page.published))
{
var publishedMember =
new XElement("member",
new XElement("name", "page_status"),
new XElement("value",
new XElement("string", page.published)
) // end value
); //end member
structElement.Add(publishedMember);
}
if (!string.IsNullOrEmpty(page.commentPolicy))
{
var publishedMember =
new XElement("member",
new XElement("name", "mt_allow_comments"),
new XElement("value",
new XElement("int", page.commentPolicy)
) // end value
); //end member
structElement.Add(publishedMember);
}
}
}
private void BuildWPPageListResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
methodParams.Add(paramElement);
var valueElement = new XElement("value");
var arrayElement = new XElement("array");
var dataElement = new XElement("data");
paramElement.Add(valueElement);
valueElement.Add(arrayElement);
arrayElement.Add(dataElement);
foreach (var page in metaWeblogResult.Pages)
{
var v = new XElement("value");
dataElement.Add(v);
var structElement = new XElement("struct");
v.Add(structElement);
var blogIdElement
= new XElement("member",
new XElement("name", "page_id"),
new XElement("value",
new XElement("string", page.pageId)
) // end value
); // end member
structElement.Add(blogIdElement);
var blogNameElement
= new XElement("member",
new XElement("name", "page_title"),
new XElement("value",
new XElement("string", page.title)
) //end value
); // end member
structElement.Add(blogNameElement);
var parentTitleElement
= new XElement("member",
new XElement("name", "wp_page_parent_title"),
new XElement("value",
new XElement("string", page.parentTitle)
) //end value
); // end member
structElement.Add(parentTitleElement);
}
}
private void BuildGetPageResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
methodParams.Add(paramElement);
var valueElement = new XElement("value");
var structElement = new XElement("struct");
paramElement.Add(valueElement);
valueElement.Add(structElement);
var postIdMember =
new XElement("member",
new XElement("name", "page_id"),
new XElement("value",
new XElement("string", metaWeblogResult.Page.pageId)
) // end value
); //end member
structElement.Add(postIdMember);
var postTitleMember =
new XElement("member",
new XElement("name", "title"),
new XElement("value",
new XElement("string", metaWeblogResult.Page.title)
) // end value
); //end member
structElement.Add(postTitleMember);
var postDescMember =
new XElement("member",
new XElement("name", "description"),
new XElement("value",
new XElement("string", metaWeblogResult.Page.description)
) // end value
); //end member
structElement.Add(postDescMember);
var postLinkMember =
new XElement("member",
new XElement("name", "link"),
new XElement("value",
new XElement("string", metaWeblogResult.Page.link)
) // end value
); //end member
structElement.Add(postLinkMember);
var breaksMember =
new XElement("member",
new XElement("name", "mt_convert_breaks"),
new XElement("value",
new XElement("string", "__default__")
) // end value
); //end member
structElement.Add(breaksMember);
if (!string.IsNullOrEmpty(metaWeblogResult.Page.pageParentId))
{
var slugMember =
new XElement("member",
new XElement("name", "wp_page_parent_id"),
new XElement("value",
new XElement("string", metaWeblogResult.Page.pageParentId)
) // end value
); //end member
structElement.Add(slugMember);
}
if (!string.IsNullOrEmpty(metaWeblogResult.Page.parentTitle))
{
var excerptMember =
new XElement("member",
new XElement("name", "wp_page_parent_title"),
new XElement("value",
new XElement("string", metaWeblogResult.Page.parentTitle)
) // end value
); //end member
structElement.Add(excerptMember);
}
if (!string.IsNullOrEmpty(metaWeblogResult.Page.pageOrder))
{
var commentPolicyMember =
new XElement("member",
new XElement("name", "wp_page_order"),
new XElement("value",
new XElement("string", metaWeblogResult.Page.pageOrder)
) // end value
); //end member
structElement.Add(commentPolicyMember);
}
if (!string.IsNullOrEmpty(metaWeblogResult.Page.published))
{
var publishedMember =
new XElement("member",
new XElement("name", "page_status"),
new XElement("value",
new XElement("string", metaWeblogResult.Page.published)
) // end value
); //end member
structElement.Add(publishedMember);
}
if (!string.IsNullOrEmpty(metaWeblogResult.Page.commentPolicy))
{
var publishedMember =
new XElement("member",
new XElement("name", "mt_allow_comments"),
new XElement("value",
new XElement("int", metaWeblogResult.Page.commentPolicy)
) // end value
); //end member
structElement.Add(publishedMember);
}
}
private void BuildNewPageResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var newElement =
new XElement("param",
new XElement("value",
new XElement("string", metaWeblogResult.PageId)
)// end value
); // end member
methodParams.Add(newElement);
}
private void BuildActionResultBoolResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var boolElement =
new XElement("param",
new XElement("value",
new XElement("boolean", metaWeblogResult.Completed ? "1" : "0")
)// end value
); // end member
methodParams.Add(boolElement);
}
private void BuildWPUserBlogsResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
var valueElement = new XElement("value");
var arrayElement = new XElement("array");
var dataElement = new XElement("data");
paramElement.Add(valueElement);
valueElement.Add(arrayElement);
arrayElement.Add(dataElement);
methodParams.Add(paramElement);
foreach (var blog in metaWeblogResult.Blogs)
{
var v = new XElement("value");
dataElement.Add(v);
var structElement = new XElement("struct");
v.Add(structElement);
var isAdminElement
= new XElement("member",
new XElement("name", "isAdmin"),
new XElement("value",
new XElement("boolean","1")
)
);
structElement.Add(isAdminElement);
var urlElement
= new XElement("member",
new XElement("name", "url"),
new XElement("value",
new XElement("string", blog.url)
) // end value
); // end member
structElement.Add(urlElement);
var blogIdElement
= new XElement("member",
new XElement("name", "blogid"),
new XElement("value",
new XElement("string", blog.blogId)
) // end value
); // end member
structElement.Add(blogIdElement);
var blogNameElement
= new XElement("member",
new XElement("name", "blogName"),
new XElement("value",
new XElement("string", blog.blogName)
) //end value
); // end member
structElement.Add(blogNameElement);
if(!string.IsNullOrEmpty(blog.xmlrpcUrl))
{
var rpcElement
= new XElement("member",
new XElement("name", "xmlrpc"),
new XElement("value",
new XElement("string", blog.xmlrpcUrl)
) //end value
); // end member
structElement.Add(rpcElement);
}
}
}
private void BuildUserBlogsResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
methodParams.Add(paramElement);
var valueElement = new XElement("value");
var arrayElement = new XElement("array");
var dataElement = new XElement("data");
paramElement.Add(valueElement);
valueElement.Add(arrayElement);
arrayElement.Add(dataElement);
foreach (var blog in metaWeblogResult.Blogs)
{
var v = new XElement("value");
dataElement.Add(v);
var structElement = new XElement("struct");
v.Add(structElement);
var urlElement
= new XElement("member",
new XElement("name", "url"),
new XElement("value", blog.url)
);
structElement.Add(urlElement);
var blogIdElement
= new XElement("member",
new XElement("name", "blogid"),
new XElement("value", blog.blogId)
);
structElement.Add(blogIdElement);
var blogNameElement
= new XElement("member",
new XElement("name", "blogName"),
new XElement("value", blog.blogName)
);
structElement.Add(blogNameElement);
}
}
private void BuildRecentPostsResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
methodParams.Add(paramElement);
var valueElement = new XElement("value");
var arrayElement = new XElement("array");
var dataElement = new XElement("data");
paramElement.Add(valueElement);
valueElement.Add(arrayElement);
arrayElement.Add(dataElement);
foreach (var post in metaWeblogResult.Posts)
{
var vElement = new XElement("value");
dataElement.Add(vElement);
var structElement = new XElement("struct");
vElement.Add(structElement);
var memberElement =
new XElement("member",
new XElement("name", "postid"),
new XElement("value",
new XElement("string", post.postId)
)// end value
); // end member
structElement.Add(memberElement);
var dateCreatedElement =
new XElement("member",
new XElement("name", "dateCreated"),
new XElement("value",
new XElement("string", Utils.ConvertDatetoISO8601(post.postDate))
)// end value
); // end member
structElement.Add(dateCreatedElement);
var titleElement =
new XElement("member",
new XElement("name", "title"),
new XElement("value",
new XElement("string", post.title)
)// end value
); // end member
structElement.Add(titleElement);
var descElement =
new XElement("member",
new XElement("name", "description"),
new XElement("value",
new XElement("string", post.description)
)// end value
); // end member
structElement.Add(descElement);
var linkElement =
new XElement("member",
new XElement("name", "link"),
new XElement("value",
new XElement("string", post.link)
)// end value
); // end member
structElement.Add(linkElement);
if (!string.IsNullOrEmpty(post.slug))
{
var slugElement =
new XElement("member",
new XElement("name", "wp_slug"),
new XElement("value",
new XElement("string", post.slug)
)// end value
); // end member
structElement.Add(slugElement);
}
if (!string.IsNullOrEmpty(post.excerpt))
{
var excerptElement =
new XElement("member",
new XElement("name", "mt_excerpt"),
new XElement("value",
new XElement("string", post.excerpt)
)// end value
); // end member
structElement.Add(excerptElement);
}
if (!string.IsNullOrEmpty(post.commentPolicy))
{
var allowCommentsElement =
new XElement("member",
new XElement("name", "mt_allow_comments"),
new XElement("value",
new XElement("string", post.commentPolicy)
)// end value
); // end member
structElement.Add(allowCommentsElement);
}
if((post.tags != null) &&(post.tags.Count > 0))
{
var tags = new string[post.tags.Count];
for (var i = 0; i < post.tags.Count; i++)
{
tags[i] = post.tags[i];
}
var tagList = string.Join(",", tags);
var tagElement =
new XElement("member",
new XElement("name", "mt_keywords"),
new XElement("value",
new XElement("string", tagList)
)// end value
); // end member
structElement.Add(tagElement);
}
var publishElement =
new XElement("member",
new XElement("name", "publish"),
new XElement("value",
new XElement("boolean", post.publish ? "1" : "0")
)// end value
); // end member
structElement.Add(publishElement);
if ((post.categories != null) &&(post.categories.Count > 0))
{
var mem = new XElement("member");
var nm = new XElement("name", "categories");
var v = new XElement("value");
var ar = new XElement("array");
var data = new XElement("data");
structElement.Add(mem);
mem.Add(nm);
mem.Add(v);
v.Add(ar);
ar.Add(data);
foreach (var cat in post.categories)
{
var val =
new XElement("value",
new XElement("string", cat)
);
data.Add(val);
}
}
}
}
private void BuildNewCategoryResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
XElement newElement =
new XElement("param",
new XElement("value",
new XElement("string", metaWeblogResult.CategoryId)
)//end value
)// end param
;
methodParams.Add(newElement);
}
private void BuildGetCategoriesResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
methodParams.Add(paramElement);
var valueElement = new XElement("value");
var arrayElement = new XElement("array");
var dataElement = new XElement("data");
paramElement.Add(valueElement);
valueElement.Add(arrayElement);
arrayElement.Add(dataElement);
foreach (var category in metaWeblogResult.Categories)
{
var vElement = new XElement("value");
dataElement.Add(vElement);
var structElement = new XElement("struct");
vElement.Add(structElement);
if(!string.IsNullOrEmpty(category.description))
{
var memberElement =
new XElement("member",
new XElement("name", "description"),
new XElement("value",
new XElement("string", category.description)
)// end value
); // end member
structElement.Add(memberElement);
}
var catIdElement =
new XElement("member",
new XElement("name", "categoryId"),
new XElement("value",
new XElement("string", category.id)
)// end value
); // end member
structElement.Add(catIdElement);
if (!string.IsNullOrEmpty(category.parentId))
{
var parentIdElement =
new XElement("member",
new XElement("name", "parentId"),
new XElement("value",
new XElement("string", category.parentId)
)// end value
); // end member
structElement.Add(parentIdElement);
}
var catTitleElement =
new XElement("member",
new XElement("name", "title"),
new XElement("value",
new XElement("string", category.title)
)// end value
); // end member
structElement.Add(catTitleElement);
var catNameElement =
new XElement("member",
new XElement("name", "categoryName"),
new XElement("value",
new XElement("string", category.title)
)// end value
); // end member
structElement.Add(catNameElement);
if (!string.IsNullOrEmpty(category.htmlUrl))
{
var htmlUrlElement =
new XElement("member",
new XElement("name", "htmlUrl"),
new XElement("value",
new XElement("string", category.htmlUrl)
)// end value
); // end member
structElement.Add(htmlUrlElement);
}
if (!string.IsNullOrEmpty(category.rssUrl))
{
var rssUrlElement =
new XElement("member",
new XElement("name", "rssUrl"),
new XElement("value",
new XElement("string", category.rssUrl)
)// end value
); // end member
structElement.Add(rssUrlElement);
}
}
}
private void BuildMediaInfoResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
XElement newElement =
new XElement("param",
new XElement("value",
new XElement("struct",
new XElement("member",
new XElement("name", "file"),
new XElement("value",
new XElement("string", metaWeblogResult.MediaInfo.file)
) // end value
), // end member
new XElement("member",
new XElement("name", "url"),
new XElement("value",
new XElement("string", metaWeblogResult.MediaInfo.url)
) // end value
), // end member
new XElement("member",
new XElement("name", "type"),
new XElement("value",
new XElement("string", metaWeblogResult.MediaInfo.type)
) // end value
) // end member
)//end struct
)//end value
)// end param
;
methodParams.Add(newElement);
}
private void BuildGetPostResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
var paramElement = new XElement("param");
methodParams.Add(paramElement);
var valueElement = new XElement("value");
var structElement = new XElement("struct");
paramElement.Add(valueElement);
valueElement.Add(structElement);
var postIdMember =
new XElement("member",
new XElement("name", "postid"),
new XElement("value",
new XElement("string", metaWeblogResult.Post.postId)
) // end value
); //end member
structElement.Add(postIdMember);
var postTitleMember =
new XElement("member",
new XElement("name", "title"),
new XElement("value",
new XElement("string", metaWeblogResult.Post.title)
) // end value
); //end member
structElement.Add(postTitleMember);
var postDescMember =
new XElement("member",
new XElement("name", "description"),
new XElement("value",
new XElement("string", metaWeblogResult.Post.description)
) // end value
); //end member
structElement.Add(postDescMember);
var postLinkMember =
new XElement("member",
new XElement("name", "link"),
new XElement("value",
new XElement("string", metaWeblogResult.Post.link)
) // end value
); //end member
structElement.Add(postLinkMember);
if(!string.IsNullOrEmpty(metaWeblogResult.Post.slug))
{
var slugMember =
new XElement("member",
new XElement("name", "wp_slug"),
new XElement("value",
new XElement("string", metaWeblogResult.Post.slug)
) // end value
); //end member
structElement.Add(slugMember);
}
if (!string.IsNullOrEmpty(metaWeblogResult.Post.excerpt))
{
var excerptMember =
new XElement("member",
new XElement("name", "mt_excerpt"),
new XElement("value",
new XElement("string", metaWeblogResult.Post.excerpt)
) // end value
); //end member
structElement.Add(excerptMember);
}
if (!string.IsNullOrEmpty(metaWeblogResult.Post.commentPolicy))
{
var commentPolicyMember =
new XElement("member",
new XElement("name", "mt_allow_comments"),
new XElement("value",
new XElement("int", metaWeblogResult.Post.commentPolicy)
) // end value
); //end member
structElement.Add(commentPolicyMember);
}
var dateCreatedMember =
new XElement("member",
new XElement("name", "dateCreated"),
new XElement("value",
new XElement("dateTime.iso8601", Utils.ConvertDatetoISO8601(metaWeblogResult.Post.postDate))
) // end value
); //end member
structElement.Add(dateCreatedMember);
var publishMember =
new XElement("member",
new XElement("name", "publish"),
new XElement("value",
new XElement("boolean", metaWeblogResult.Post.publish ? "1" : "0")
) // end value
); //end member
structElement.Add(publishMember);
if((metaWeblogResult.Post.tags != null) &&(metaWeblogResult.Post.tags.Count > 0))
{
var tags = new string[metaWeblogResult.Post.tags.Count];
for (var i = 0; i < metaWeblogResult.Post.tags.Count; i++)
{
tags[i] = metaWeblogResult.Post.tags[i];
}
var tagList = string.Join(",", tags);
var tagsMember =
new XElement("member",
new XElement("name", "mt_keywords"),
new XElement("value",
new XElement("string", tagList)
) // end value
); //end member
structElement.Add(tagsMember);
}
if ((metaWeblogResult.Post.categories != null) &&(metaWeblogResult.Post.categories.Count > 0))
{
var categoriesMember = new XElement("member");
structElement.Add(categoriesMember);
var catName = new XElement("name", "categories");
categoriesMember.Add(catName);
var catValueMember = new XElement("value");
categoriesMember.Add(catValueMember);
var catArrayMember = new XElement("array");
catValueMember.Add(catArrayMember);
var catDataMember = new XElement("data");
catArrayMember.Add(catDataMember);
foreach (var cat in metaWeblogResult.Post.categories)
{
var v = new XElement("value",
new XElement("string", cat)
);
catDataMember.Add(v);
}
}
}
private void BuildNewPostResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
XElement newElement =
new XElement("param",
new XElement("value",
new XElement("string", metaWeblogResult.PostId)
)//end value
)// end param
;
methodParams.Add(newElement);
}
private void BuildWPGetFeaturesResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
XElement newElement =
new XElement("param",
new XElement("value",
new XElement("array",
new XElement("data",
new XElement("value",
new XElement("struct",
new XElement("member",
new XElement("name", "videopress_enabled"),
new XElement("value",
new XElement("boolean","0")
)// end value
)//end member
)//end struct
)//end value
)// end data
)//end array
)//end value
)// end param
;
methodParams.Add(newElement);
}
private void BuildListMethodsResponse(XElement methodParams, MetaWeblogResult metaWeblogResult)
{
XElement newElement =
new XElement("param",
new XElement("value",
new XElement("array",
new XElement("data",
// these are not really all supported but I'm listing them as supported to see which
// ones are actually called by blogging clients
// like the Wordpress iPad app, BlogPress, and Blogsy
new XElement("value",
new XElement("string", "system.multicall")
), //end value
new XElement("value",
new XElement("string", "system.listMethods")
) ,//end value
new XElement("value",
new XElement("string", "system.getCapabilities")
), //end value
new XElement("value",
new XElement("string", "pingback.extensions.getPingbacks")
), //end value
new XElement("value",
new XElement("string", "pingback.ping")
), //end value
new XElement("value",
new XElement("string", "mt.publishPost")
), //end value
new XElement("value",
new XElement("string", "mt.getTrackbackPings")
), //end value
new XElement("value",
new XElement("string", "mt.supportedTextFilters")
), //end value
new XElement("value",
new XElement("string", "mt.supportedMethods")
), //end value
new XElement("value",
new XElement("string", "mt.setPostCategories")
), //end value
new XElement("value",
new XElement("string", "mt.getPostCategories")
), //end value
new XElement("value",
new XElement("string", "mt.getRecentPostTitles")
), //end value
new XElement("value",
new XElement("string", "mt.getCategoryList")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.getUsersBlogs")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.deletePost")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.newMediaObject")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.setTemplate")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.getTemplate")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.getCategories")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.getRecentPosts")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.getPost")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.editPost")
), //end value
new XElement("value",
new XElement("string", "metaWeblog.newPost")
), //end value
new XElement("value",
new XElement("string", "blogger.deletePost")
), //end value
new XElement("value",
new XElement("string", "blogger.editPost")
), //end value
new XElement("value",
new XElement("string", "blogger.newPost")
), //end value
new XElement("value",
new XElement("string", "blogger.setTemplate")
), //end value
new XElement("value",
new XElement("string", "blogger.getTemplate")
), //end value
new XElement("value",
new XElement("string", "blogger.getRecentPosts")
), //end value
new XElement("value",
new XElement("string", "blogger.getPost")
), //end value
new XElement("value",
new XElement("string", "blogger.getUserInfo")
), //end value
new XElement("value",
new XElement("string", "blogger.getUsersBlogs")
), //end value
new XElement("value",
new XElement("string", "wp.newPage")
), //end value
new XElement("value",
new XElement("string", "wp.getPageList")
), //end value
new XElement("value",
new XElement("string", "wp.getPages")
), //end value
new XElement("value",
new XElement("string", "wp.getPage")
), //end value
new XElement("value",
new XElement("string", "wp.editPage")
), //end value
new XElement("value",
new XElement("string", "wp.deletePage")
), //end value
new XElement("value",
new XElement("string", "wp.getUsersBlogs")
), //end value
new XElement("value",
new XElement("string", "wp.getCategories")
), //end value
new XElement("value",
new XElement("string", "wp.deleteCategory")
), //end value
new XElement("value",
new XElement("string", "wp.newCategory")
), //end value
new XElement("value",
new XElement("string", "wp.suggestCategories")
), //end value
new XElement("value",
new XElement("string", "wp.getTags")
), //end value
new XElement("value",
new XElement("string", "wp.uploadFile")
), //end value
new XElement("value",
new XElement("string", "wp.getCommentStatusList")
), //end value
new XElement("value",
new XElement("string", "wp.newComment")
), //end value
new XElement("value",
new XElement("string", "wp.editComment")
), //end value
new XElement("value",
new XElement("string", "wp.deleteComment")
), //end value
new XElement("value",
new XElement("string", "wp.getComments")
), //end value
new XElement("value",
new XElement("string", "wp.getComment")
), //end value
new XElement("value",
new XElement("string", "wp.getCommentCount")
), //end value
new XElement("value",
new XElement("string", "wp.setOptions")
), //end value
new XElement("value",
new XElement("string", "wp.getOptions")
), //end value
new XElement("value",
new XElement("string", "wp.getPageTemplates")
), //end value
new XElement("value",
new XElement("string", "wp.getPageStatusList")
), //end value
new XElement("value",
new XElement("string", "wp.getPostStatusList")
), //end value
new XElement("value",
new XElement("string", "wp.getAuthors")
), //end value
new XElement("value",
new XElement("string", "wp.getMediaLibrary")
), //end value
new XElement("value",
new XElement("string", "wp.getMediaItem")
) //end value
)// end data
)//end array
)//end value
)// end param
;
methodParams.Add(newElement);
}
private void BuildFaultResponse(XElement methodResponse, MetaWeblogResult metaWeblogResult)
{
XElement faultElement =
new XElement("fault",
new XElement("value",
new XElement("struct",
new XElement("member",
new XElement("name","faultCode"),
new XElement("value",
new XElement("int", metaWeblogResult.Fault.faultCode)
)// end value
)// end member
,
new XElement("member",
new XElement("name", "faultString"),
new XElement("value",
new XElement("string", metaWeblogResult.Fault.faultString)
)// end value
) //end member
)//end struct
)//end value
);
methodResponse.Add(faultElement);
}
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/MetaWeblogSecurityResult.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public class MetaWeblogSecurityResult
{
public MetaWeblogSecurityResult(
string displayName,
string blogId,
bool isAuthenticated,
bool canEditPosts,
bool canEditPages
)
{
this.displayName = displayName;
this.blogId = blogId;
this.isAuthenticated = isAuthenticated;
this.canEditPosts = canEditPosts;
this.canEditPages = canEditPages;
}
private string displayName = string.Empty;
private string blogId = string.Empty;
private bool isAuthenticated = false;
private bool canEditPosts = false;
private bool canEditPages = false;
public string DisplayName
{
get { return displayName; }
}
public string BlogId
{
get { return blogId; }
}
public bool IsAuthenticated
{
get { return isAuthenticated; }
}
public bool CanEditPosts
{
get { return canEditPosts; }
}
public bool CanEditPages
{
get { return canEditPages; }
}
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/ApiOptions.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog.Models
{
public class ApiOptions
{
///
/// only for debugging purposes would you ever set either of these true
/// don't leave it as true
///
public bool DumpRequestXmlToDisk { get; set; } = false;
public bool DumpResponseXmlToDisk { get; set; } = false;
public string AppRootDumpFolderVPath { get; set; } = "/cloudscribe_config/data_xml/metaweblogxmldumps/";
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/AuthorStruct.cs
================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog.Models
{
///
/// wp Author struct
///
public struct AuthorStruct
{
public string display_name;
public string meta_value;
public string user_email;
public string user_id;
public string user_login;
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/BlogInfoStruct.cs
================================================
namespace cloudscribe.MetaWeblog.Models
{
///
/// MetaWeblog BlogInfo struct
/// returned as an array from getUserBlogs
///
public struct BlogInfoStruct
{
public string blogId;
public string blogName;
public string url;
public string xmlrpcUrl;
//had these in mojoportal not sure we will use them
public string pageEditRoles;
public string moduleEditRoles;
public int editUserId;
public int pageId;
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/CategoryStruct.cs
================================================
namespace cloudscribe.MetaWeblog.Models
{
///
/// MetaWeblog Category struct
/// returned as an array from GetCategories
///
public struct CategoryStruct
{
public string description;
public string htmlUrl;
public string id;
public string parentId;
public string rssUrl;
public string title;
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/FaultStruct.cs
================================================
namespace cloudscribe.MetaWeblog.Models
{
///
/// MetaWeblog Fault struct
/// returned when error occurs
///
public struct FaultStruct
{
public string faultCode;
public string faultString;
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/MediaInfoStruct.cs
================================================
namespace cloudscribe.MetaWeblog.Models
{
///
/// MetaWeblog MediaInfo struct
/// returned from NewMediaObject call
///
public struct MediaInfoStruct
{
///
/// Url that points to Saved MediaObejct
///
public string url;
public string file;
///
/// Type of file
///
public string type;
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/MediaObjectStruct.cs
================================================
namespace cloudscribe.MetaWeblog.Models
{
///
/// MetaWeblog MediaObject struct
/// passed in the newMediaObject call
///
public struct MediaObjectStruct
{
///
/// Media object bytes
///
public byte[] bytes;
///
/// Name of media object (filename)
///
public string name;
///
/// Type of file
///
public string type;
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/MetaWeblogException.cs
================================================
using System;
namespace cloudscribe.MetaWeblog.Models
{
public class MetaWeblogException : Exception
{
///
/// Initializes a new instance of the class.
/// Constructor to load properties
///
///
/// Fault code to be returned in Fault Response
///
///
/// Message to be returned in Fault Response
///
public MetaWeblogException(string code, string message): base(message)
{
this.Code = code;
}
///
/// Gets code is actually for Fault Code. It will be passed back in the
/// response along with the error message.
///
public string Code { get; private set; }
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/PageStruct.cs
================================================
using System;
namespace cloudscribe.MetaWeblog.Models
{
///
/// wp Page Struct
///
public struct PageStruct
{
///
/// Content of Blog Post
///
public string description;
///
/// Link to Blog Post
///
public string link;
//wp_slug
public string slug;
///
/// Convert Breaks
///
public string mt_convert_breaks;
///
/// Page keywords
///
public string mt_keywords;
///
/// Display date of Blog Post (DateCreated)
///
public DateTime pageDate;
public DateTime pageUtcDate;
///
/// PostID Guid in string format
///
public string pageId;
///
/// Page Parent ID
///
public string pageParentId;
public string parentTitle;
//string page_status
///
/// PageOrder
///
public string pageOrder;
///
/// Title of Blog Post
///
public string title;
///
/// CommentPolicy (Allow/Deny)
///
public string commentPolicy;
public string published; //publish or draft
//http://codex.wordpress.org/XML-RPC_wp
//TODO: implement support for custom fields
// ? will live writer round trip these?
// we need a place to store the module id of the hmtl item
//array custom_fields : struct string id string key string value
//public string moduleId;
//public string itemId;
public string pageEditRoles;
public string moduleEditRoles;
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/PostStruct.cs
================================================
using System;
using System.Collections.Generic;
namespace cloudscribe.MetaWeblog.Models
{
///
/// MetaWeblog Post struct
/// used in newPost, editPost, getPost, recentPosts
/// not all properties are used everytime.
///
public struct PostStruct
{
///
/// wp_author_id
///
public string author;
///
/// List of Categories assigned for Blog Post
///
public List categories;
///
/// CommentPolicy (Allow/Deny)
/// this would only be used on outgoing structs to indicate to the client
/// whether comments are allowed
///
public string commentPolicy;
///
/// Content of Blog Post
///
public string description;
///
/// Excerpt
///
public string excerpt;
///
/// Link to Blog Post
/// this would only be populated on outgoing structs
/// we pass this to the cient, the client doesn't pass it to us
///
public string link;
///
/// Display date of Blog Post (DateCreated)
///
public DateTime postDate;
//public DateTime dateCreated;
///
/// PostID Guid in string format
///
public string postId;
///
/// Whether the Post is published or not.
///
public bool publish;
///
/// Slug of post
///
public string slug;
///
/// List of Tags assigned for Blog Post
///
/// cloudscribe.SimpleContent is not currently supporting tags as a separate concept from categories
/// caregories are essentially similar to tags
/// tags maps to wordpress keywords, I'm guessin that was once upon a time used to populate
/// meta but has been ignored and devalued by search engines for many years so
/// nobody does that anymore afaik
///
public List tags;
///
/// Title of Blog Post
///
public string title;
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Models/UserInfoStruct.cs
================================================
namespace cloudscribe.MetaWeblog.Models
{
///
/// MetaWeblog UserInfo struct
/// returned from GetUserInfo call
///
///
/// Not used currently, but here for completeness.
///
public class UserInfoStruct
{
///
/// User Name Proper
///
public string nickname;
///
/// Login ID
///
public string userID;
///
/// Url to User Blog?
///
public string url;
///
/// Email address of User
///
public string email;
///
/// User LastName
///
public string lastName;
///
/// User First Name
///
public string firstName;
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/README.md
================================================
# cloudscribe.MetaWeblog
[](https://www.nuget.org/packages/cloudscribe.MetaWeblog)
[](https://opensource.org/licenses/Apache-2.0)
MetaWeblog API implementation for cloudscribe SimpleContent, enabling integration with blogging clients.
## Installation
```shell
Install-Package cloudscribe.MetaWeblog
```
## Usage
Add this package to your ASP.NET Core project to enable MetaWeblog API support for SimpleContent.
## Contributing
Contributions are welcome! Please see the [contributing guidelines](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/CONTRIBUTING.md).
## License
Licensed under the Apache 2.0 License. See the [LICENSE](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/LICENSE) file for details.
================================================
FILE: src/cloudscribe.MetaWeblog/ServiceCollectionExtensions.cs
================================================
using cloudscribe.MetaWeblog;
using cloudscribe.MetaWeblog.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
namespace Microsoft.Extensions.DependencyInjection
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddCloudscribeMetaWeblog(
this IServiceCollection services,
IConfiguration configuration = null
)
{
if (configuration != null)
{
services.Configure(configuration);
}
else
{
services.TryAddSingleton();
}
services.TryAddScoped();
services.TryAddScoped();
services.TryAddScoped();
services.TryAddScoped();
return services;
}
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/Utils.cs
================================================
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
namespace cloudscribe.MetaWeblog
{
public static class Utils
{
public static string GetDateTimeStringForFileName()
{
return GetDateTimeStringForFileName(false);
}
public static string GetDateTimeStringForFileName(bool includeMiliseconds)
{
DateTime d = DateTime.Now;
string dateString = d.Year.ToInvariantString();
string monthString = d.Month.ToInvariantString();
if (monthString.Length == 1)
{
monthString = "0" + monthString;
}
string dayString = d.Day.ToInvariantString();
if (dayString.Length == 1)
{
dayString = "0" + dayString;
}
string hourString = d.Hour.ToInvariantString();
if (hourString.Length == 1)
{
hourString = "0" + hourString;
}
string minuteString = d.Minute.ToInvariantString();
if (minuteString.Length == 1)
{
minuteString = "0" + minuteString;
}
string secondString = d.Second.ToInvariantString();
if (secondString.Length == 1)
{
secondString = "0" + secondString;
}
dateString
= dateString
+ monthString
+ dayString
+ hourString
+ minuteString + secondString;
if (includeMiliseconds)
{
return dateString + d.Millisecond.ToInvariantString();
}
return dateString;
}
public static string ToInvariantString(this int i)
{
return i.ToString(CultureInfo.InvariantCulture);
}
public static string ConvertDatetoISO8601(DateTime date)
{
var temp = string.Format(
"{0}{1}{2}T{3}:{4}:{5}",
date.Year,
date.Month.ToString().PadLeft(2, '0'),
date.Day.ToString().PadLeft(2, '0'),
date.Hour.ToString().PadLeft(2, '0'),
date.Minute.ToString().PadLeft(2, '0'),
date.Second.ToString().PadLeft(2, '0'));
return temp;
}
//iso8601 often come in slightly different flavours rather than the standard "s" that string.format supports.
//http://stackoverflow.com/a/17752389
//static readonly string[] formats = {
// // Basic formats
// "yyyyMMddTHHmmsszzz",
// "yyyyMMddTHHmmsszz",
// "yyyyMMddTHHmmssZ",
// // Extended formats
// "yyyy-MM-ddTHH:mm:sszzz",
// "yyyy-MM-ddTHH:mm:sszz",
// "yyyy-MM-ddTHH:mm:ssZ",
// "yyyyMMddTHH:mm:ss:zzz",
// "yyyyMMddTHH:mm:ss:zz",
// "yyyyMMddTHH:mm:ss:Z",
// "yyyyMMddTHH:mm:ss",
// // All of the above with reduced accuracy
// "yyyyMMddTHHmmzzz",
// "yyyyMMddTHHmmzz",
// "yyyyMMddTHHmmZ",
// "yyyy-MM-ddTHH:mmzzz",
// "yyyy-MM-ddTHH:mmzz",
// "yyyy-MM-ddTHH:mmZ",
// // Accuracy reduced to hours
// "yyyyMMddTHHzzz",
// "yyyyMMddTHHzz",
// "yyyyMMddTHHZ",
// "yyyy-MM-ddTHHzzz",
// "yyyy-MM-ddTHHzz",
// "yyyy-MM-ddTHHZ"
//};
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/XmlResult.cs
================================================
using Microsoft.AspNetCore.Mvc;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
//http://tech-journals.com/jonow/2012/01/25/implementing-xml-rpc-services-with-asp-net-mvc
//http://www.aaron-powell.com/posts/2010-06-16-aspnet-mvc-xml-action-result.html
//http://www.aaron-powell.com/posts/2010-06-16-aspnet-mvc-xml-action-result.html
//https://github.com/myquay/Chq.XmlRpc.Mvc
namespace cloudscribe.MetaWeblog
{
public class XmlResult : ActionResult
{
public XDocument Xml { get; private set; }
public string ContentType { get; set; }
//public Encoding Encoding { get; set; }
public XmlResult(XDocument xml)
{
this.Xml = xml;
this.ContentType = "text/xml";
}
public override async Task ExecuteResultAsync(ActionContext context)
{
context.HttpContext.Response.ContentType = this.ContentType;
if (Xml != null)
{
using (var ms = new MemoryStream())
{
await Xml.SaveAsync(ms, SaveOptions.DisableFormatting, CancellationToken.None);
var bytes = ms.ToArray();
await context.HttpContext.Response.BodyWriter.WriteAsync(bytes);
}
}
else
{
await base.ExecuteResultAsync(context);
}
}
}
}
================================================
FILE: src/cloudscribe.MetaWeblog/cloudscribe.MetaWeblog.csproj
================================================
a re-useable implementation of the metaweblog api for asp.net core
10.1.0
net10.0
Joe Audette
cloudscribe;metaweblog;api;asp.net core
icon.png
https://github.com/cloudscribe/cloudscribe.MetaWeblog
Apache-2.0
https://github.com/cloudscribe/cloudscribe.MetaWeblog.git
git
README.md
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/README.md
================================================
# cloudscribe.SimpleContent.CompiledViews.Bootstrap5
[](https://www.nuget.org/packages/cloudscribe.SimpleContent.CompiledViews.Bootstrap5)
[](https://opensource.org/licenses/Apache-2.0)
Precompiled Bootstrap 5 views for cloudscribe.SimpleContent.
## Installation
```shell
Install-Package cloudscribe.SimpleContent.CompiledViews.Bootstrap5
```
## Usage
Add this package to your ASP.NET Core project to use Bootstrap 5 precompiled views with SimpleContent.
## Contributing
Contributions are welcome! Please see the [contributing guidelines](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/CONTRIBUTING.md).
## License
Licensed under the Apache 2.0 License. See the [LICENSE](https://github.com/cloudscribe/cloudscribe.SimpleContent/blob/main/LICENSE) file for details.
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/VersionProvider.cs
================================================
using cloudscribe.Versioning;
using cloudscribe.Web.Common;
using System;
using System.Reflection;
namespace cloudscribe.SimpleContent.CompiledViews.Bootstrap5
{
public class VersionProvider : IVersionProvider
{
public string Name { get { return "cloudscribe.SimpleContent.CompiledViews.Bootstrap5"; } }
public Guid ApplicationId { get { return new Guid("f94067b4-919d-4910-acd1-4b3b1c210ecf"); } }
public Version CurrentVersion
{
get
{
var version = new Version(2, 0, 0, 0);
var versionString = typeof(CloudscribeCommonResources).Assembly.GetCustomAttribute().Version;
if (!string.IsNullOrWhiteSpace(versionString))
{
Version.TryParse(versionString, out version);
}
return version;
}
}
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/AboutPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject IStringLocalizer sr
@inject IBlogRoutes blogRoutes
@inject ISimpleContentThemeHelper themeHelper
@{
var themeSettings = themeHelper.GetThemeSettings();
var heading = Model.ProjectSettings.AboutHeading;
if(string.IsNullOrWhiteSpace(heading))
{
heading = sr["About"];
}
}
@heading
@if (Model.CanEdit && string.IsNullOrWhiteSpace(Model.ProjectSettings.AboutContent))
{
@sr["You can edit this from Administration > Content Settings"]
}
@Html.Raw(Model.ProjectSettings.AboutContent)
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/Archive.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject IUrlHelper myhelper
@{
ViewData["AsidePrimaryVisible"] = Model.ProjectSettings.ShowBlogCategories || Model.ProjectSettings.ShowArchivedPosts || Model.ProjectSettings.ShowRelatedPosts || Model.ProjectSettings.ShowAboutBox ? true : false; ;
ViewData["AsidePrimaryToggle"] = "show";
}
@section Meta {
}
@section Styles {
}
@if (Model.ProjectSettings.ShowTitle)
{
}
@if (Model.ProjectSettings.ShowBlogCategories || Model.ProjectSettings.ShowArchivedPosts || Model.ProjectSettings.ShowRelatedPosts || Model.ProjectSettings.ShowAboutBox)
{
@section AsidePrimary {
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/ArchiveListPartial.cshtml
================================================
@model Dictionary
@inject IStringLocalizer sr
@inject IBlogRoutes blogRoutes
@inject ISimpleContentThemeHelper themeHelper
@{
var themeSettings = themeHelper.GetThemeSettings();
}
@if (Model.Count() > 0)
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/BlogHeaderPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Models.ProjectSettings
@{
string headerClass = (Model.ShowTitle) ? headerClass = "" : "class=visually-hidden";
}
@if (Model.ShowTitle)
{
@Model.Description
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/BlogMetaPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@using cloudscribe.Web.Common.Extensions
@{
var imageUrl = Model.ExtractFirstImageUrl(Model.CurrentPost, Url);
}
@if (Model.CurrentPost != null && !string.IsNullOrEmpty(Model.CurrentPost.MetaDescription))
{
@if (!string.IsNullOrEmpty(imageUrl))
{
}
@if (!string.IsNullOrWhiteSpace(Model.ProjectSettings.SiteName))
{
}
@if (!string.IsNullOrWhiteSpace(Model.ProjectSettings.FacebookAppId))
{
}
@if (!string.IsNullOrWhiteSpace(Model.ProjectSettings.TwitterPublisher))
{
}
@if (!string.IsNullOrWhiteSpace(Model.ProjectSettings.TwitterCreator))
{
}
@if (!string.IsNullOrEmpty(imageUrl))
{
}
}
@*
TODO: implement this stuff
RSD: Discoverability of Blog APIs
http://mashupguide.net/1.0/html/ch05s06.xhtml
http://en.wikipedia.org/wiki/Really_Simple_Discovery
*@
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/BlogScriptsPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@if (Model.Template != null)
{
@foreach (var c in Model.Template.RenderScripts.Where(x => (x.Environment == "dev" || x.Environment == "any")).OrderBy(x => x.Sort).ThenBy(x => x.Url).ToList())
{
}
@foreach (var c in Model.Template.RenderScripts.Where(x => (x.Environment == "prod" || x.Environment == "any")).OrderBy(x => x.Sort).ThenBy(x => x.Url).ToList())
{
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/BlogStylePartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@if (Model.Template != null)
{
@foreach (var c in Model.Template.RenderCss.Where(x => (x.Environment == "dev" || x.Environment == "any")).OrderBy(x => x.Sort).ThenBy(x => x.Url).ToList())
{
}
@foreach (var c in Model.Template.RenderCss.Where(x => (x.Environment == "prod" || x.Environment == "any")).OrderBy(x => x.Sort).ThenBy(x => x.Url).ToList())
{
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/CategoryListPartial.cshtml
================================================
@model Dictionary
@inject IStringLocalizer sr
@inject IBlogRoutes blogRoutes
@inject ISimpleContentThemeHelper themeHelper
@{
var themeSettings = themeHelper.GetThemeSettings();
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/CommentLinkDetail.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject ISimpleContentThemeHelper themeHelper
@inject IStringLocalizer sr
@{
var themeSettings = themeHelper.GetThemeSettings();
var commentFrag = GetCommentString(Model, themeSettings);
DateTime? pubDate = Model.CurrentPost.PubDate;
if (!pubDate.HasValue) { pubDate = DateTime.UtcNow; }
}
@functions{
public string GetCommentString(cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel model, SimpleContentThemeSettings themeSettings)
{
if (string.IsNullOrEmpty(Model.ProjectSettings.DisqusShortName))
{
if (model.CurrentPost.ApprovedCommentCount() == 0)
{
return string.Empty;
//return " " + "";
}
return " " + Model.CurrentPost.ApprovedCommentCount() + " " + sr["Comments"];
}
return " " + "";
}
public string GetCommentFragment()
{
if (string.IsNullOrEmpty(Model.ProjectSettings.DisqusShortName))
{
return "comments";
}
return "disqus_thread";
}
}
@if (!string.IsNullOrWhiteSpace(commentFrag))
{
@if (Model.ProjectSettings.IncludePubDateInPostUrls)
{
@Html.Raw(commentFrag)
}
else
{
@Html.Raw(commentFrag))
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/CommentLinkList.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject ISimpleContentThemeHelper themeHelper
@inject IStringLocalizer sr
@{
var themeSettings = themeHelper.GetThemeSettings();
var commentFrag = GetCommentString(Model, themeSettings, Model.TmpPost);
DateTime? pubDate = Model.TmpPost.PubDate;
if (!pubDate.HasValue) { pubDate = DateTime.UtcNow; }
}
@functions{
public string GetCommentString(cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel model, SimpleContentThemeSettings themeSettings, IPost post)
{
if (string.IsNullOrEmpty(Model.ProjectSettings.DisqusShortName))
{
if (post.ApprovedCommentCount() == 0)
{
return string.Empty;
}
return " " + post.ApprovedCommentCount() + " " + sr["Comments"];
}
return " " + "";
}
public string GetCommentFragment()
{
if (string.IsNullOrEmpty(Model.ProjectSettings.DisqusShortName))
{
return "comments";
}
return "disqus_thread";
}
}
@if (!string.IsNullOrWhiteSpace(commentFrag))
{
@if (Model.ProjectSettings.IncludePubDateInPostUrls)
{
@Html.Raw(commentFrag)
}
else
{
@Html.Raw(commentFrag))
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/CommentWrapperPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject IStringLocalizer sr
@if ((Model.CurrentPost != null) && (!string.IsNullOrEmpty(Model.CurrentPost.Id)) && (Model.ShowComments))
{
@if ((Model.CommentsAreOpen) || (Model.CanEdit && Model.CurrentPost.CommentCount() > 0) || (Model.CurrentPost.ApprovedCommentCount() > 0))
{
}
@if (Model.CommentsAreOpen)
{
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/DisqusCommentsPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@using Microsoft.AspNetCore.Http.Features
@inject IStringLocalizer sr
@{
var consentFeature = Context.Features.Get();
var allowDisqus = consentFeature?.CanTrack ?? true; // disqus has cookies
var disqusScriptPath = "//" + Model.ProjectSettings.DisqusShortName + ".disqus.com/embed.js";
}
@if (allowDisqus && (Model.CurrentPost != null) && (!string.IsNullOrEmpty(Model.CurrentPost.Id)) && (Model.ShowComments))
{
@sr["Comments"]
}
@if(!allowDisqus)
{
@sr["If you want to view or submit comments you must accept the cookie consent."]
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/DraftInfoPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject IStringLocalizer sr
@inject IBlogRoutes blogRoutes
@{
DateTime? pubDate = Model.CurrentPost.PubDate;
if (!pubDate.HasValue) { pubDate = DateTime.UtcNow; }
}
@if (Model.ShowingDraft && Model.CanEdit)
{
@sr["You are currently viewing a draft version of this post."]
@if (Model.CurrentPost.PubDate.HasValue)
{
@sr["Scheduled Publish Date:"] @Model.FormatDateForEdit(Model.CurrentPost.PubDate.Value)
}
@if (Model.HasPublishedVersion)
{
@if (Model.ProjectSettings.IncludePubDateInPostUrls)
{
@sr["View published version."]
}
else
{
@sr["View published version."]
}
}
}
else if (Model.HasDraft && Model.CanEdit)
{
@if (Model.ProjectSettings.IncludePubDateInPostUrls)
{
@sr["View current draft."]
}
else
{
@sr["View current draft."]
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/Edit.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject IStringLocalizer sr
@inject IBlogRoutes blogRoutes
@{
var contentId = Model.Id;
if (string.IsNullOrWhiteSpace(contentId)) { contentId = "draft-post-" + DateTime.UtcNow.ToString("yyyy-MM-dd-hh"); }
}
@section Styles {
}
@ViewBag.Title
@if (!string.IsNullOrEmpty(Model.Id))
{
@sr["Version History"]
}
@if (!string.IsNullOrEmpty(Model.Id))
{
}
@sr["To publish later you must enter a date."]
@section Scripts {
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditContentHtmlPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject ISummernoteOptionsResolver summernoteOptionsResolver
@inject ICkeditorOptionsResolver editorOptionsResolver
@{
var summernoteOptions = await summernoteOptionsResolver.GetSummernoteOptions();
var ckOptions = await editorOptionsResolver.GetCkeditorOptions();
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditContentMarkdownPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject ISummernoteOptionsResolver summernoteOptionsResolver
@inject ICkeditorOptionsResolver editorOptionsResolver
@inject IStringLocalizer sr
@{
var summernoteOptions = await summernoteOptionsResolver.GetSummernoteOptions();
var ckOptions = await editorOptionsResolver.GetCkeditorOptions();
var contentId = Model.Id;
if (string.IsNullOrWhiteSpace(contentId)) { contentId = "draft-post-" + DateTime.UtcNow.ToString("yyyy-MM-dd-hh"); }
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditCorrelationKeyPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject IStringLocalizer sr
@* comment this and uncomment the below if you want editable correlationkey *@
@*
*@
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditDraftPubDatePartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject IStringLocalizer sr
@{
var message = "";
if(Model.DraftPubDate.HasValue)
{
var format = sr["This content is scheduled to be published {0}. You can still edit and save changes to this draft before it is published by clicking \"Save Draft\""].ToString();
message = string.Format(format, Model.DraftPubDate.Value);
}
}
@if (Model.DraftPubDate.HasValue)
{
@message
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditHistoryInfoPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject IStringLocalizer sr
@if (Model.HistoryId.HasValue)
{
@if (Model.DidReplaceDraft)
{
@sr["A previous version of this post has been restored to the editor, replacing the current draft version. If this is not intended, do not save/publish this version."]
}
@if (Model.DidRestoreDeleted)
{
@sr["You've just restored a previously deleted post to the editor; click save/publish to re-create the post. If this is not intended, do not save/publish this version."]
}
@if (!Model.DidReplaceDraft && !Model.DidRestoreDeleted)
{
@sr["A previous version of this post has been restored to the editor. If this is not intended, do not save/publish this version."]
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditImageUrlPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject IStringLocalizer sr
@* comment this and uncomment the below if you want editable image/thumbnail *@
@*
*@
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditPubDate.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject IStringLocalizer sr
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditScriptsPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@if (Model.ContentType == "markdown")
{
}
else
{
}
@**@
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditSlugPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject IStringLocalizer sr
@* comment this and uncomment the below if you want editable slug*@
@*
*@
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditStylesPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@if (Model.ContentType == "markdown")
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditTeaserDisabledPartial.cshtml
================================================
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditTeaserHtmlPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject ISummernoteOptionsResolver summernoteOptionsResolver
@inject ICkeditorOptionsResolver editorOptionsResolver
@inject IStringLocalizer sr
@{
var summernoteOptions = await summernoteOptionsResolver.GetSummernoteOptions();
var ckOptions = await editorOptionsResolver.GetCkeditorOptions();
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditTeaserMarkdownPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditViewModel
@inject ISummernoteOptionsResolver summernoteOptionsResolver
@inject ICkeditorOptionsResolver editorOptionsResolver
@inject IStringLocalizer sr
@{
var summernoteOptions = await summernoteOptionsResolver.GetSummernoteOptions();
var ckOptions = await editorOptionsResolver.GetCkeditorOptions();
var contentId = Model.Id + "-teaser";
if (string.IsNullOrWhiteSpace(contentId)) { contentId = "draft-post-teaser-" + DateTime.UtcNow.ToString("yyyy-MM-dd-hh"); }
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplate.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@inject IStringLocalizer sr
@inject IBlogRoutes blogRoutes
@{
var contentId = Model.Id;
if (string.IsNullOrWhiteSpace(contentId)) { contentId = "draft-post-" + DateTime.UtcNow.ToString("yyyy-MM-dd-hh"); }
var legend = sr["Template Fields"].ToString();
var legendFormat = sr["Template Fields - {0}"].ToString();
if (Model.Template != null)
{
legend = string.Format(legendFormat, Model.Template.Title);
}
}
@section Styles {
}
@ViewBag.Title
@if (!string.IsNullOrEmpty(Model.Id))
{
@sr["Version History"]
}
@if (!string.IsNullOrEmpty(Model.Id))
{
}
@sr["To publish later you must enter a date."]
@section Scripts {
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplateCorrelationKeyPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@inject IStringLocalizer sr
@* comment this and uncomment the below if you want editable correlationkey *@
@*
*@
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplateDraftPubDatePartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@inject IStringLocalizer sr
@{
var message = "";
if(Model.DraftPubDate.HasValue)
{
var format = sr["This content is scheduled to be published {0}. You can still edit and save changes to this draft before it is published by clicking \"Save Draft\""].ToString();
message = string.Format(format, Model.DraftPubDate.Value);
}
}
@if (Model.DraftPubDate.HasValue)
{
@message
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplateHistoryInfoPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@inject IStringLocalizer sr
@if (Model.HistoryId.HasValue)
{
@if (Model.DidReplaceDraft)
{
@sr["A previous version of this post has been restored to the editor, replacing the current draft version. If this is not intended, do not save/publish this version."]
}
@if (Model.DidRestoreDeleted)
{
@sr["You've just restored a previously deleted post to the editor; click save/publish to re-create the post. If this is not intended, do not save/publish this version."]
}
@if (!Model.DidReplaceDraft && !Model.DidRestoreDeleted)
{
@sr["A previous version of this post has been restored to the editor. If this is not intended, do not save/publish this version."]
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplateImageUrlPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@inject IStringLocalizer sr
@* comment this and uncomment the below if you want editable image/thumbnail *@
@*
*@
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplatePubDate.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@inject IStringLocalizer sr
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplateScriptsPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@foreach (var c in Model.Template.EditScripts.Where(x => (x.Environment == "dev" || x.Environment == "any")).OrderBy(x => x.Sort).ThenBy(x => x.Url).ToList())
{
}
@foreach (var c in Model.Template.EditScripts.Where(x => (x.Environment == "prod" || x.Environment == "any")).OrderBy(x => x.Sort).ThenBy(x => x.Url).ToList())
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplateSlugPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@inject IStringLocalizer sr
@* comment this and uncomment the below if you want editable slug*@
@*
*@
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplateStylesPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@foreach (var c in Model.Template.EditCss.Where(x => (x.Environment == "dev" || x.Environment == "any")).OrderBy(x => x.Sort).ThenBy(x => x.Url).ToList())
{
}
@foreach (var c in Model.Template.EditCss.Where(x => (x.Environment == "prod" || x.Environment == "any")).OrderBy(x => x.Sort).ThenBy(x => x.Url).ToList())
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/EditWithTemplateTeaserHtmlPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PostEditWithTemplateViewModel
@inject ISummernoteOptionsResolver summernoteOptionsResolver
@inject ICkeditorOptionsResolver editorOptionsResolver
@inject IStringLocalizer sr
@{
var summernoteOptions = await summernoteOptionsResolver.GetSummernoteOptions();
var ckOptions = await editorOptionsResolver.GetCkeditorOptions();
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/History.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.ContentHistoryViewModel
@inject IStringLocalizer sr
@inject IBlogRoutes blogRoutes
@{
var format = sr["{0} - Content Edit History"].ToString();
ViewData["Title"] = string.Format(format, Model.ContentTitle);
}
@ViewData["Title"]
@sr["View current version"]
@if (Model.CanEditPosts)
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/HistoryInfoPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject IStringLocalizer sr
@inject IBlogRoutes blogRoutes
@if (Model.HistoryId.HasValue && Model.HistoryArchiveDate.HasValue)
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/Index.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject IUrlHelper myhelper
@inject IStringLocalizer sr
@{
var disqusCommentCountScriptPath = "//" + Model.ProjectSettings.DisqusShortName + ".disqus.com/count.js";
ViewData["AsidePrimaryVisible"] = Model.ProjectSettings.ShowBlogCategories || Model.ProjectSettings.ShowArchivedPosts || Model.ProjectSettings.ShowRelatedPosts || Model.ProjectSettings.ShowAboutBox ? true : false;
ViewData["AsidePrimaryToggle"] = "show";
}
@section Meta {
}
@section Styles {
}
@section Toolbar {
}
@if (Model.ProjectSettings.ShowBlogCategories || Model.ProjectSettings.ShowArchivedPosts || Model.ProjectSettings.ShowRelatedPosts || Model.ProjectSettings.ShowAboutBox)
{
@section AsidePrimary {
}
}
@section Scripts {
@if (!string.IsNullOrEmpty(Model.ProjectSettings.DisqusShortName))
{
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/NewPost.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.NewContentViewModel
@inject IStringLocalizer sr
@inject IBlogRoutes blogRoutes
@{
ViewData["Title"] = sr["Create a New Post"];
}
@ViewData["Title"]
@sr["New Post with Standard Content Editor"]
@if (Model.CountOfTemplates > 0)
{
@sr["Create Post From Template"]
}
@section Scripts {
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/NextPreviousPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject IStringLocalizer sr
@inject ISimpleContentThemeHelper themeHelper
@{
var themeSettings = themeHelper.GetThemeSettings();
}
@functions{
public string GetDivClass()
{
if ((!string.IsNullOrEmpty(Model.PreviousPostUrl)) && (!string.IsNullOrEmpty(Model.NextPostUrl)))
{
return "col-4";
}
return "col";
}
}
@if ((!string.IsNullOrEmpty(Model.PreviousPostUrl)) || (!string.IsNullOrEmpty(Model.NextPostUrl)))
{
@if (!string.IsNullOrEmpty(Model.PreviousPostUrl))
{
}
@if (!string.IsNullOrEmpty(Model.NextPostUrl))
{
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/Post.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@{
var disqusCommentCountScriptPath = "//" + Model.ProjectSettings.DisqusShortName + ".disqus.com/count.js";
ViewData["AsidePrimaryVisible"] = Model.ProjectSettings.ShowBlogCategories || Model.ProjectSettings.ShowArchivedPosts || Model.ProjectSettings.ShowRelatedPosts || Model.ProjectSettings.ShowAboutBox ? true : false; ;
ViewData["AsidePrimaryToggle"] = "show";
}
@section Meta {
}
@section Styles {
}
@section Toolbar {
}
@if (Model.ProjectSettings.ShowTitle)
{
}
@if (Model.ProjectSettings.ShowBlogCategories || Model.ProjectSettings.ShowArchivedPosts || Model.ProjectSettings.ShowRelatedPosts || Model.ProjectSettings.ShowAboutBox)
{
@section AsidePrimary {
}
}
@section Scripts {
@if (!string.IsNullOrEmpty(Model.ProjectSettings.DisqusShortName))
{
}
else if (Model.CommentsAreOpen)
{
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/PostContentBodyPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject IStringLocalizer sr
@inject IContentTemplateService templateService;
@{
string dynamicViewName = null;
object templateModel = null;
if (Model.CurrentPost != null && !string.IsNullOrWhiteSpace(Model.CurrentPost.TemplateKey))
{
var template = await templateService.GetTemplate(Model.CurrentPost.BlogId, Model.CurrentPost.TemplateKey);
if (template != null && !string.IsNullOrWhiteSpace(template.DynamicRenderPartialView))
{
templateModel = templateService.DesrializeTemplateModel(Model.CurrentPost, template);
dynamicViewName = template.DynamicRenderPartialView;
}
}
}
@if (templateModel != null && !string.IsNullOrWhiteSpace(dynamicViewName))
{
}
else
{
@Html.Raw(Model.FilterHtml(Model.CurrentPost))
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/PostDetailPartial.cshtml
================================================
@using cloudscribe.SimpleContent.Web
@using cloudscribe.SimpleContent.Models
@using Microsoft.AspNetCore.Mvc.ViewFeatures
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@using cloudscribe.SimpleContent.Web.Design
@using Microsoft.Extensions.Options
@using Microsoft.Extensions.Localization
@using System.Globalization
@inject IStringLocalizer sr
@inject ISimpleContentThemeHelper themeHelper
@{
var themeSettings = themeHelper.GetThemeSettings();
}
@functions{
public string GetPubDateClass()
{
if (Model.CurrentPost != null && Model.CurrentPost.PubDate > DateTime.UtcNow) return "lead text-danger";
return null;
}
}
@{
var imageUrl = Model.ExtractFirstImageUrl(Model.CurrentPost, Url);
var imageSize = Model.ExtractFirstImageDimensions(Model.CurrentPost);
}
@if (!string.IsNullOrEmpty(imageUrl))
{
}
@if (!string.IsNullOrEmpty(Model.CurrentPost.Author))
{
}
@if (!string.IsNullOrEmpty(Model.ProjectSettings.Publisher))
{
}
@if (Model.CurrentPost.PubDate.HasValue)
{
}
@if (Model.CurrentPost.IsPublished && !Model.ShowingDraft && !Model.HistoryId.HasValue)
{
@if (string.IsNullOrEmpty(Model.ProjectSettings.DisqusShortName))
{
}
else
{
}
}
@if (Model.CurrentPost != null && Model.CanEdit && !Model.ShowingDeleted && !Model.ShowingDraft && !Model.HistoryId.HasValue && Model.CurrentPost.IsPublished)
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/PostListPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@foreach (var post in Model.Posts)
{
Model.TmpPost = post;
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/PostPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject IStringLocalizer sr
@inject ISimpleContentThemeHelper themeHelper
@{
var themeSettings = themeHelper.GetThemeSettings();
}
@functions {
public string GetPubDateClass()
{
if (Model.TmpPost != null && Model.TmpPost.PubDate > DateTime.UtcNow) return "lead text-danger";
return null;
}
}
@if (!Model.TmpPost.IsPublished)
{
@sr["This is an unpublished draft."]
}
else if (Model.CanEdit && Model.TmpPost.HasDraftVersion())
{
@sr["This is the published version, but this post has an unpublished draft."]
}
@{
var result = Model.FilterHtmlForList(Model.TmpPost);
}
@Html.Raw(result.FilteredContent)
@if (!result.IsFullContent)
{@if (Model.ProjectSettings.IncludePubDateInPostUrls)
{
@sr["[...Read More]"]
}
else
{
@sr["[...Read More]"]
}
}
@{
var imageUrl = Model.ExtractFirstImageUrl(Model.TmpPost, Url);
var imageSize = Model.ExtractFirstImageDimensions(Model.TmpPost);
}
@if (!string.IsNullOrEmpty(imageUrl))
{
}
@if (!string.IsNullOrEmpty(Model.TmpPost.Author))
{
}
@if (!string.IsNullOrEmpty(Model.ProjectSettings.Publisher))
{
}
@if (Model.TmpPost.PubDate.HasValue)
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/RelatedPostsPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@{
}
@if (Model.CurrentPost != null && Model.CurrentPost.Categories.Count > 0)
{
@await Component.InvokeAsync("RelatedPosts", new { currentPostId = Model.CurrentPost.Id, viewName = "RelatedPosts", numberToShow = 5 })
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/SideBarPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@if (Model.ProjectSettings.ShowAboutBox)
{
}
@if (Model.ProjectSettings.ShowBlogCategories)
{
}
@if (Model.ProjectSettings.ShowArchivedPosts)
{
}
@if (Model.ProjectSettings.ShowRelatedPosts)
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Blog/ToolsPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.BlogViewModel
@inject ISimpleContentThemeHelper themeHelper
@inject IStringLocalizer sr
@inject IOptions editOptionsAccessor
@{
var themeSettings = themeHelper.GetThemeSettings();
var editOptions = editOptionsAccessor.Value;
}
@if (Model.CanEdit)
{
@if (Model.CurrentPost != null)
{
}
@if (!editOptions.HideUnpublishButton && Model.CurrentPost != null && Model.CanEdit && !Model.ShowingDeleted && !Model.ShowingDraft && !Model.HistoryId.HasValue && Model.CurrentPost.IsPublished)
{
}
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/ContentHistory/Index.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.ContentHistoryViewModel
@inject IStringLocalizer sr
@{
ViewData["Title"] = sr["Content Edit History"];
}
@ViewData["Title"]
@if (Model.CanEditPages && Model.CanEditPosts)
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Page/AddCssPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.AddPageResourceViewModel
@inject IPageRoutes pageRoutes
@inject IStringLocalizer sr
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Page/AddJsPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.AddPageResourceViewModel
@inject IPageRoutes pageRoutes
@inject IStringLocalizer sr
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Page/ChildMenu.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PageViewModel
@inject IRoleSelectorProperties roleSelector
@section Meta {
}
@section Styles {
}
@section Toolbar {
}
@if (Model.CurrentPage.ShowHeading)
{
@ViewData["Title"]
}
@await Component.InvokeAsync("Navigation", new { viewName = "ChildTree", filterName = NamedNavigationFilters.ChildTree, startingNodeKey = "" })
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Page/CommentWrapperPartial.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PageViewModel
@if ((Model.CurrentPage.ShowComments) && (!string.IsNullOrEmpty(Model.ProjectSettings.DisqusShortName)) && !Model.ShowingDraft && !Model.HistoryId.HasValue)
{
}
================================================
FILE: src/cloudscribe.SimpleContent.CompiledViews.Bootstrap5/Views/Page/Development.cshtml
================================================
@model cloudscribe.SimpleContent.Web.ViewModels.PageDevelopmentViewModel
@inject IPageRoutes pageRoutes
@inject IStringLocalizer sr
@sr["With great power comes great responsibility. This page is intended for use by developers, for adding javscript and css into the page. Use this page only if you understand what you are doing."]
@sr["Raw JavaScript"]
@sr["Comments"]
@foreach (var comment in Model.CurrentPost.Comments) { Model.TmpComment = comment; if (comment.IsApproved || !Model.ProjectSettings.ModerateComments || Model.CanEdit) {