Merge remote-tracking branch 'origin/dev-v7' into dev-v7.7

This commit is contained in:
Shannon
2017-09-12 13:38:34 +10:00
7 changed files with 271 additions and 17 deletions

View File

@@ -1,12 +1,19 @@
using System; using System;
using System.Linq; using System.Collections.Generic;
using System.IO;
using System.Web;
using System.Xml.Linq; using System.Xml.Linq;
using ClientDependency.Core.CompositeFiles.Providers;
using ClientDependency.Core.Config;
using Umbraco.Core.IO; using Umbraco.Core.IO;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
namespace Umbraco.Core.Configuration namespace Umbraco.Core.Configuration
{ {
internal class ClientDependencyConfiguration /// <summary>
/// A utility class for working with CDF config and cache files - use sparingly!
/// </summary>
public class ClientDependencyConfiguration
{ {
private readonly ILogger _logger; private readonly ILogger _logger;
private readonly string _fileName; private readonly string _fileName;
@@ -21,7 +28,7 @@ namespace Umbraco.Core.Configuration
/// <summary> /// <summary>
/// Changes the version number in ClientDependency.config to a random value to avoid stale caches /// Changes the version number in ClientDependency.config to a random value to avoid stale caches
/// </summary> /// </summary>
internal bool IncreaseVersionNumber() public bool IncreaseVersionNumber()
{ {
try try
{ {
@@ -49,5 +56,55 @@ namespace Umbraco.Core.Configuration
return false; return false;
} }
/// <summary>
/// Clears the temporary files stored for the ClientDependency folder
/// </summary>
/// <param name="currentHttpContext"></param>
public bool ClearTempFiles(HttpContextBase currentHttpContext)
{
var cdfTempDirectories = new HashSet<string>();
foreach (BaseCompositeFileProcessingProvider provider in ClientDependencySettings.Instance
.CompositeFileProcessingProviderCollection)
{
var path = provider.CompositeFilePath.FullName;
cdfTempDirectories.Add(path);
}
try
{
var fullPath = currentHttpContext.Server.MapPath(XmlFileMapper.FileMapVirtualFolder);
if (fullPath != null)
{
cdfTempDirectories.Add(fullPath);
}
}
catch (Exception ex)
{
//invalid path format or something... try/catch to be safe
LogHelper.Error<ClientDependencyConfiguration>("Could not get path from ClientDependency.config", ex);
}
var success = true;
foreach (var directory in cdfTempDirectories)
{
var directoryInfo = new DirectoryInfo(directory);
if (directoryInfo.Exists == false)
continue;
try
{
directoryInfo.Delete(true);
}
catch (Exception ex)
{
// Something could be locking the directory or the was another error, making sure we don't break the upgrade installer
LogHelper.Error<ClientDependencyConfiguration>("Could not clear temp files", ex);
success = false;
}
}
return success;
}
} }
} }

View File

@@ -45,6 +45,9 @@
<HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath> <HintPath>..\packages\AutoMapper.3.3.1\lib\net40\AutoMapper.Net4.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="ClientDependency.Core, Version=1.9.2.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ClientDependency.1.9.2\lib\net45\ClientDependency.Core.dll</HintPath>
</Reference>
<Reference Include="HtmlAgilityPack, Version=1.4.9.5, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL"> <Reference Include="HtmlAgilityPack, Version=1.4.9.5, Culture=neutral, PublicKeyToken=bd319b19eaf3b43a, processorArchitecture=MSIL">
<HintPath>..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll</HintPath> <HintPath>..\packages\HtmlAgilityPack.1.4.9.5\lib\Net45\HtmlAgilityPack.dll</HintPath>
<Private>True</Private> <Private>True</Private>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <packages>
<package id="AutoMapper" version="3.3.1" targetFramework="net45" /> <package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="ClientDependency" version="1.9.2" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.9.5" targetFramework="net45" /> <package id="HtmlAgilityPack" version="1.4.9.5" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.3" targetFramework="net45" /> <package id="ImageProcessor" version="2.5.3" targetFramework="net45" />
<package id="log4net" version="2.0.8" targetFramework="net45" /> <package id="log4net" version="2.0.8" targetFramework="net45" />

View File

@@ -21,6 +21,21 @@ namespace Umbraco.Tests.FrontEnd
Assert.AreEqual("Hello world, this is some&hellip;", result); Assert.AreEqual("Hello world, this is some&hellip;", result);
} }
/// <summary>
/// If a truncated string ends with a space, we should trim the space before appending the ellipsis.
/// </summary>
[Test]
public void Truncate_Simple_With_Trimming()
{
var text = "Hello world, this is some text <a href='blah'>with a link</a>";
var helper = new UmbracoHelper();
var result = helper.Truncate(text, 26).ToString();
Assert.AreEqual("Hello world, this is some&hellip;", result);
}
[Test] [Test]
public void Truncate_Inside_Word() public void Truncate_Inside_Word()
{ {
@@ -78,5 +93,79 @@ namespace Umbraco.Tests.FrontEnd
Assert.AreEqual(expectedResult, result); Assert.AreEqual(expectedResult, result);
} }
[Test]
public void Truncate_By_Words()
{
var text = "Hello world, this is some text <a href='blah'>with a link</a>";
var helper = new UmbracoHelper();
var result = helper.TruncateByWords(text, 4).ToString();
Assert.AreEqual("Hello world, this is&hellip;", result);
}
[Test]
public void Truncate_By_Words_With_Tag()
{
var text = "Hello world, <b>this</b> is some text <a href='blah'>with a link</a>";
var helper = new UmbracoHelper();
var result = helper.TruncateByWords(text, 4).ToString();
Assert.AreEqual("Hello world, <b>this</b> is&hellip;", result);
}
[Test]
public void Truncate_By_Words_Mid_Tag()
{
var text = "Hello world, this is some text <a href='blah'>with a link</a>";
var helper = new UmbracoHelper();
var result = helper.TruncateByWords(text, 7).ToString();
Assert.AreEqual("Hello world, this is some text <a href='blah'>with&hellip;</a>", result);
}
[Test]
public void Strip_All_Html()
{
var text = "Hello world, <b>this</b> is some text <a href='blah'>with a link</a>";
var helper = new UmbracoHelper();
var result = helper.StripHtml(text, null).ToString();
Assert.AreEqual("Hello world, this is some text with a link", result);
}
[Test]
public void Strip_Specific_Html()
{
var text = "Hello world, <b>this</b> is some text <a href='blah'>with a link</a>";
string [] tags = {"b"};
var helper = new UmbracoHelper();
var result = helper.StripHtml(text, tags).ToString();
Assert.AreEqual("Hello world, this is some text <a href='blah'>with a link</a>", result);
}
[Test]
public void Strip_Invalid_Html()
{
var text = "Hello world, <bthis</b> is some text <a href='blah'>with a link</a>";
var helper = new UmbracoHelper();
var result = helper.StripHtml(text).ToString();
Assert.AreEqual("Hello world, is some text with a link", result);
}
} }
} }

View File

@@ -3,8 +3,10 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Text.RegularExpressions;
using System.Web; using System.Web;
using HtmlAgilityPack; using HtmlAgilityPack;
using Umbraco.Web.WebApi.Filters;
namespace Umbraco.Web namespace Umbraco.Web
{ {
@@ -30,6 +32,7 @@ namespace Umbraco.Web
{ {
var doc = new HtmlDocument(); var doc = new HtmlDocument();
doc.LoadHtml("<p>" + html + "</p>"); doc.LoadHtml("<p>" + html + "</p>");
var targets = new List<HtmlNode>(); var targets = new List<HtmlNode>();
var nodes = doc.DocumentNode.FirstChild.SelectNodes(".//*"); var nodes = doc.DocumentNode.FirstChild.SelectNodes(".//*");
@@ -55,7 +58,7 @@ namespace Umbraco.Web
{ {
return new HtmlString(html); return new HtmlString(html);
} }
return new HtmlString(doc.DocumentNode.FirstChild.InnerHtml); return new HtmlString(doc.DocumentNode.FirstChild.InnerHtml.Replace(" ", " "));
} }
internal string Join<TIgnore>(string seperator, params object[] args) internal string Join<TIgnore>(string seperator, params object[] args)
@@ -85,6 +88,8 @@ namespace Umbraco.Web
public IHtmlString Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent) public IHtmlString Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent)
{ {
const string hellip = "&hellip;";
using (var outputms = new MemoryStream()) using (var outputms = new MemoryStream())
{ {
using (var outputtw = new StreamWriter(outputms)) using (var outputtw = new StreamWriter(outputms))
@@ -106,7 +111,7 @@ namespace Umbraco.Web
isTagClose = false; isTagClose = false;
int ic = 0, int ic = 0,
currentLength = 0, //currentLength = 0,
currentTextLength = 0; currentTextLength = 0;
string currentTag = string.Empty, string currentTag = string.Empty,
@@ -141,6 +146,10 @@ namespace Umbraco.Web
{ {
string thisTag = tagStack.Pop(); string thisTag = tagStack.Pop();
outputtw.Write("</" + thisTag + ">"); outputtw.Write("</" + thisTag + ">");
if (treatTagsAsContent)
{
currentTextLength++;
}
} }
if (!isTagClose && currentTag.Length > 0) if (!isTagClose && currentTag.Length > 0)
{ {
@@ -148,6 +157,10 @@ namespace Umbraco.Web
{ {
tagStack.Push(currentTag); tagStack.Push(currentTag);
outputtw.Write("<" + currentTag); outputtw.Write("<" + currentTag);
if (treatTagsAsContent)
{
currentTextLength++;
}
if (!string.IsNullOrEmpty(tagContents)) if (!string.IsNullOrEmpty(tagContents))
{ {
if (tagContents.EndsWith("/")) if (tagContents.EndsWith("/"))
@@ -205,7 +218,7 @@ namespace Umbraco.Web
{ {
var charToWrite = (char)ic; var charToWrite = (char)ic;
outputtw.Write(charToWrite); outputtw.Write(charToWrite);
currentLength++; //currentLength++;
} }
} }
@@ -221,7 +234,7 @@ namespace Umbraco.Web
// Reached truncate limit. // Reached truncate limit.
if (addElipsis) if (addElipsis)
{ {
outputtw.Write("&hellip;"); outputtw.Write(hellip);
} }
lengthReached = true; lengthReached = true;
} }
@@ -235,10 +248,59 @@ namespace Umbraco.Web
outputms.Position = 0; outputms.Position = 0;
using (TextReader outputtr = new StreamReader(outputms)) using (TextReader outputtr = new StreamReader(outputms))
{ {
return new HtmlString(outputtr.ReadToEnd().Replace(" ", " ").Trim()); string result = string.Empty;
string firstTrim = outputtr.ReadToEnd().Replace(" ", " ").Trim();
//Check to see if there is an empty char between the hellip and the output string
//if there is, remove it
if (string.IsNullOrWhiteSpace(firstTrim) == false)
{
result = firstTrim[firstTrim.Length - hellip.Length - 1] == ' ' ? firstTrim.Remove(firstTrim.Length - hellip.Length - 1, 1) : firstTrim;
}
return new HtmlString(result);
} }
} }
} }
} }
/// <summary>
/// Returns the length of the words from a html block
/// </summary>
/// <param name="html">Html text</param>
/// <param name="words">Amount of words you would like to measure</param>
/// <param name="tagsAsContent"></param>
/// <returns></returns>
public int WordsToLength(string html, int words)
{
HtmlDocument doc = new HtmlDocument();
doc.LoadHtml(html);
int wordCount = 0,
length = 0,
maxWords = words;
html = StripHtmlTags(html, null).ToString();
while (length < html.Length)
{
// Check to see if the current wordCount reached the maxWords allowed
if (wordCount.Equals(maxWords)) break;
// Check if current char is part of a word
while (length < html.Length && char.IsWhiteSpace(html[length]) == false)
{
length++;
}
wordCount++;
// Skip whitespace until the next word
while (length < html.Length && char.IsWhiteSpace(html[length]) && wordCount.Equals(maxWords) == false)
{
length++;
}
}
return length;
}
} }
} }

View File

@@ -47,6 +47,8 @@ namespace Umbraco.Web.Install.Controllers
// Update ClientDependency version // Update ClientDependency version
var clientDependencyConfig = new ClientDependencyConfiguration(ApplicationContext.Current.ProfilingLogger.Logger); var clientDependencyConfig = new ClientDependencyConfiguration(ApplicationContext.Current.ProfilingLogger.Logger);
var clientDependencyUpdated = clientDependencyConfig.IncreaseVersionNumber(); var clientDependencyUpdated = clientDependencyConfig.IncreaseVersionNumber();
// Delete ClientDependency temp directories to make sure we get fresh caches
var clientDependencyTempFilesDeleted = clientDependencyConfig.ClearTempFiles(HttpContext);
var result = _umbracoContext.Security.ValidateCurrentUser(false); var result = _umbracoContext.Security.ValidateCurrentUser(false);

View File

@@ -1439,18 +1439,58 @@ namespace Umbraco.Web
/// </summary> /// </summary>
public IHtmlString Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent) public IHtmlString Truncate(string html, int length, bool addElipsis, bool treatTagsAsContent)
{ {
return _stringUtilities.Truncate(html, length, addElipsis, treatTagsAsContent); return _stringUtilities.Truncate(html, length, addElipsis, treatTagsAsContent);
} }
#region Truncate by Words
/// <summary>
/// Truncates a string to a given amount of words, can add a elipsis at the end (...). Method checks for open html tags, and makes sure to close them
/// </summary>
public IHtmlString TruncateByWords(string html, int words)
{
int length = _stringUtilities.WordsToLength(html, words);
#endregion return Truncate(html, length, true, false);
}
#region If /// <summary>
/// Truncates a string to a given amount of words, can add a elipsis at the end (...). Method checks for open html tags, and makes sure to close them
/// </summary>
public IHtmlString TruncateByWords(string html, int words, bool addElipsis)
{
int length = _stringUtilities.WordsToLength(html, words);
/// <summary> return Truncate(html, length, addElipsis, false);
/// If the test is true, the string valueIfTrue will be returned, otherwise the valueIfFalse will be returned. }
/// </summary>
public HtmlString If(bool test, string valueIfTrue, string valueIfFalse) /// <summary>
/// Truncates a string to a given amount of words, can add a elipsis at the end (...). Method checks for open html tags, and makes sure to close them
/// </summary>
public IHtmlString TruncateByWords(IHtmlString html, int words)
{
int length = _stringUtilities.WordsToLength(html.ToHtmlString(), words);
return Truncate(html, length, true, false);
}
/// <summary>
/// Truncates a string to a given amount of words, can add a elipsis at the end (...). Method checks for open html tags, and makes sure to close them
/// </summary>
public IHtmlString TruncateByWords(IHtmlString html, int words, bool addElipsis)
{
int length = _stringUtilities.WordsToLength(html.ToHtmlString(), words);
return Truncate(html, length, addElipsis, false);
}
#endregion
#endregion
#region If
/// <summary>
/// If the test is true, the string valueIfTrue will be returned, otherwise the valueIfFalse will be returned.
/// </summary>
public HtmlString If(bool test, string valueIfTrue, string valueIfFalse)
{ {
return test ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse); return test ? new HtmlString(valueIfTrue) : new HtmlString(valueIfFalse);
} }