Compare commits

...

83 Commits

Author SHA1 Message Date
Sebastiaan Jansssen 6ec90a3acd Merge branch 'dev-v7.6' into master-v76 2018-01-08 12:45:20 +01:00
Sebastiaan Jansssen 3556043b55 Update to latest modelsbuilder 2018-01-08 12:44:29 +01:00
Sebastiaan Janssen 054ac78970 Revert "Position changed from "target" to "center""
This reverts commit c1769aa299.
2017-11-15 14:39:09 +01:00
Sebastiaan Janssen b3cc5f9183 Merge branch 'dev-v7.6' into master-v76 2017-11-14 11:43:03 +01:00
Sebastiaan Janssen 2b3397120f Disambiguate events by explicitly naming them 2017-11-14 11:23:48 +01:00
Sebastiaan Janssen ce99ec6d31 Merge branch 'dev-v7.6' into master-v76 2017-11-14 07:21:48 +01:00
Sebastiaan Janssen 350f5c88c7 Bump version to 7.6.12 2017-11-14 07:16:14 +01:00
Stephan 928c0dc535 Merge pull request #2314 from umbraco/temp-U4-10646
U4-10646 Sorting nodes in edge cases can lead to blanking out of property data
2017-11-13 17:33:27 +01:00
Sebastiaan Janssen cc7a28db65 Update to latest ImageProcessor 2017-11-13 15:39:29 +01:00
Sebastiaan Janssen 065e764957 Update to latest Examine 2017-11-13 15:39:28 +01:00
Shannon 637abc384b more test hacks 2017-11-08 18:46:08 +11:00
Shannon 8959b81f6a another test fix attempt (this is only for 7.6, this is already fixed in 7.7) 2017-11-08 18:16:17 +11:00
Shannon 32bb8ac5a4 Merge branch 'dev-v7.6' of https://github.com/umbraco/Umbraco-CMS into dev-v7.6 2017-11-08 17:47:32 +11:00
Shannon 016caba35f attempt to fix tests 2017-11-08 17:47:18 +11:00
Shannon Deminick d522dd7a31 Merge pull request #2313 from umbraco/temp-U4-10620
U4-10620 Umbraco 2FA implementation issues
2017-11-08 17:18:31 +11:00
Shannon 95f632e1ea Adds notes and overrides SendTwoFactorCodeAsync and checks for -1 instead of null 2017-11-08 17:16:11 +11:00
Shannon Deminick e72ec171f7 Merge pull request #2312 from umbraco/temp-U4-9857
Temp u4 9857
2017-11-08 16:06:53 +11:00
Shannon 2538e2e9c9 additional null check 2017-11-08 16:06:23 +11:00
Daniël Knippers b1ceeb23b3 Only show characters left is a number of characters is configured on the datatype, hide otherwise 2017-11-07 23:13:59 +01:00
Sebastiaan Janssen 579c572dd8 Oops, missed that I was passing a string array into the int array param 2017-11-07 18:36:37 +01:00
Sebastiaan Janssen bbf4c18825 U4-10646 Sorting nodes in edge cases can lead to blanking out of property data 2017-11-07 17:12:04 +01:00
Sebastiaan Janssen 9a94ac5317 Implement TwoFactorSignInAsync from Identity repo to make sure our custom GetVerifiedUserIdAsync fires
U4-10620 Umbraco 2FA implementation issues
2017-11-07 16:51:40 +01:00
Sebastiaan Janssen 0b904e82b5 Get user by id instead of loginname
U4-9857 Umbraco 7.6 - Exception after changing user login name
2017-11-07 16:36:15 +01:00
Sebastiaan Janssen 51fec0c7d3 Remove misleading text, nothing bad will happen when you close the sort panel, sorting is a background task. 2017-11-07 14:03:17 +01:00
Sebastiaan Janssen 87ace12a4f Prevent double clicks of the Save button
U4-10646 Sorting nodes in edge cases can lead to blanking out of property data
2017-11-07 13:49:02 +01:00
Daniël Knippers a1fd73cdb0 Feature Request: Double Click Content Node to Expand
Doube-clicking a tree item now expands or collapses a node. It basically does the exact same as clicking the triangle left of the node.

(cherry picked from commit bd2a794b59)
2017-11-07 10:30:08 +01:00
Sebastiaan Janssen 403502ef1b Merge pull request #2294 from ja0b/U4-854
U4-854 Add MaxLength to Textstring fields --> Fixed

(cherry picked from commit ee4ad36e70)
2017-11-06 17:11:12 +01:00
Craig c1769aa299 Position changed from "target" to "center"
Prevents modal jumping position on HIDPI screens

(cherry picked from commit 422be31c4e)
2017-11-06 14:58:31 +01:00
prjseal 143d127fb9 Format date in backoffice based on Users language
We have formatted the Created Date and Updated Date using the Current
Culture of the application.

(cherry picked from commit 10ca1239b6)
2017-11-06 14:03:54 +01:00
Lewis Smith 01cf2e240a U4-10627 - Updated stats / facts when installing.
U4-10627 Update some of the Umbraco Installer facts. Updated the facts you get when installing umbraco. All facts that were changed were taken from the Umbraco page.

https://umbraco.com/blog/codegarden-2017-the-aftermath/
https://umbraco.com/blog/codegarden-2017/
(cherry picked from commit 69258fee7b)
2017-11-06 13:51:43 +01:00
Tim 49689d5057 Fixes missing content type check.
(cherry picked from commit 80d605beac)
2017-11-06 13:48:09 +01:00
Andy Felton f722d7731d Fixed URL Tracker button colour
(cherry picked from commit bb9a0258df)
2017-11-06 13:47:05 +01:00
Sebastiaan Janssen ac42f335c2 Fix U4-4889 checkbox in template 2017-11-06 12:33:12 +01:00
Sebastiaan Janssen f0eefbd5af U4-10636 Removed '#' from 'value' when generating alias 2017-11-06 12:31:06 +01:00
Sebastiaan Janssen a8b660bfbf Check for itemMinHeight on scope before trying to set it 2017-11-02 17:46:01 +01:00
Sebastiaan Janssen dc7b5bae9b Fix U4-10618 - AbstractConfigCheck SuccessMessages are missing CurrentValue 2017-11-02 16:28:58 +01:00
Claus 30b391f3fb Merge pull request #2281 from umbraco/temp-U4-10622
U4-10622 Corrupted id/key map
2017-11-02 14:56:35 +01:00
Shannon ff3b7963b1 U4-10622 Corrupted id/key map 2017-11-02 15:35:47 +11:00
Sebastiaan Janssen 84b380028c Fixes U4-9401 Double slashes in some Umbraco generated urls causes broken links 2017-10-31 23:21:41 +01:00
Sebastiaan Janssen f0b5c831f1 Manually applied all changes for U4-10503 Umbraco plugins cache files should be stored in the same local temp location as the umbraco xml cache file 2017-10-25 14:05:56 +02:00
Shannon d3232a09fb U4-10503 Umbraco plugins cache files should be stored in the same local temp location as the umbraco xml cache file
(cherry picked from commit 7b3f7f4ad4)
2017-10-25 13:42:10 +02:00
Shannon 7cbaecc53a U4-10538 Disable Ping on front-end nodes when load balancing
(cherry picked from commit fd8daa398f)
2017-10-25 13:03:31 +02:00
Stephan e84a963d66 U4-10121 - Scheduled Publishing as background task
(cherry picked from commit 79df75c415)
2017-10-25 11:51:42 +02:00
Sebastiaan Janssen 88d61a4d37 Merge branch 'dev-v7.6' into master-v76 2017-10-23 14:15:42 +02:00
Sebastiaan Janssen a36184a261 Bump version to 7.6.11 2017-10-23 14:14:59 +02:00
Sebastiaan Janssen 75f4535157 Merge branch 'dev-v7.6' into master-v76 2017-10-23 14:11:26 +02:00
Claus 9c50e1c48d fixing bug in U4-10524 Show warning in UI for the media/content picker that is referencing a trashed or deleted item 2017-10-17 23:52:34 +02:00
Sebastiaan Janssen 78bf75615f Merge branch 'dev-v7.6' into master-v76 2017-10-17 11:04:27 +02:00
Sebastiaan Janssen 9411a22a0d Don't forget to call the ConfigureUmbracoAuthentication method 2017-10-17 11:04:01 +02:00
Sebastiaan Janssen 51649879cd Merge branch 'dev-v7.6' into master-v76 2017-10-17 11:00:28 +02:00
Sebastiaan Janssen 461ce64feb Bump version to 7.6.10 2017-10-17 10:57:18 +02:00
Sebastiaan Janssen ab050eb7ac Merge branch 'dev-v7.6' into master-v76 2017-10-17 10:55:56 +02:00
Sebastiaan Janssen c5a55b17e6 Manually applying U4-10369: Split out ConfigureUmbracoAuthentication method 2017-10-17 10:08:37 +02:00
Sebastiaan Janssen fd577afe2c Html encode nodenames to prevent XSS attacks. Fixes U4-10497 XSS Vulnerability in page name.
(cherry picked from commit fe2b86b681)
2017-10-16 20:32:56 +02:00
Sebastiaan Janssen f46ef0b006 U4-10506 Importing a specially crafted document type file can cause XXE attack
(cherry picked from commit 5dde2efe0d)
2017-10-16 20:32:04 +02:00
Sebastiaan Janssen dc937ec942 Fix the "Add media" box no longer being square 2017-10-16 16:13:47 +02:00
Shannon cefd9e7323 Fixes PostAddFolder
(cherry picked from commit c5d874464a)

# Conflicts:
#	src/Umbraco.Web/Editors/MediaController.cs
2017-10-16 15:53:54 +02:00
Mads Rasmussen 315690e758 fixes breadcrumbs and search when start node is set
(cherry picked from commit 1ec07aa205)
2017-10-16 15:14:24 +02:00
Mads Rasmussen fb8c519f71 fixes: U4-10521 Creating new folders from the media picker no longer works
(cherry picked from commit 1cc3b39601)

# Conflicts:
#	src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html
2017-10-16 15:13:56 +02:00
Warren Buckley 5df4d3509d Fix in PowerShell build - The 7Zip tool download was giving two paths & thus break the build script
(cherry picked from commit a2e6da2491)
2017-10-16 14:22:09 +02:00
Claus 4bd263e7bc U4-10524 Show warning in UI for the media/content picker that is referencing a trashed or deleted item
U4-10533 Show warning in UI for the link picker that is referencing a trashed or deleted item
cherrypicked from dev-v7.
2017-10-16 13:52:17 +02:00
Shannon 916bc6badb U4-10539 - reverts merge issue that reverted the fix for U4-9262
(cherry picked from commit 2a0cc71d06)
2017-10-16 08:38:17 +02:00
Sebastiaan Janssen 2461924a89 Merge branch 'master-v76' of https://github.com/umbraco/Umbraco-CMS into master-v76 2017-10-02 13:53:41 +02:00
Sebastiaan Janssen 581f15b172 Manually applying change from U4-10478 7.7.1 HtmlStringUtilities Truncate throws an IndexOutOfRange when input word is shorter than "…" and crash the entire site to 7.6.9 too. 2017-10-01 12:05:40 +02:00
Shannon 33d7598d3c Adding key fallback to list view select functions to fix issue when selecting multiple items with an ID of int.MaxValue 2017-09-29 13:16:53 +10:00
Sebastiaan Janssen 476773fce5 Merge pull request #2224 from umbraco/temp-U4-5222
U4-5222 Rollback Feature fails to update the UI to show the rolled ba…
2017-09-28 20:00:13 +02:00
Sebastiaan Janssen 953355cddd Merge pull request #2222 from umbraco/temp-U4-10364-2
U4-10364 SQL error occurs when we attempt to sort media items by "Cre…
2017-09-28 19:39:19 +02:00
Shannon d2d43255aa U4-5222 Rollback Feature fails to update the UI to show the rolled back content - requires refresh 2017-09-28 02:38:53 +10:00
Shannon b39e218d51 U4-10364 SQL error occurs when we attempt to sort media items by "Created by" column 2017-09-27 23:04:53 +10:00
Sebastiaan Janssen 4b581a2182 Merge pull request #2217 from umbraco/temp-U4-10470
U4-10470 Missing transform for the scripts tree when upgrading
2017-09-27 10:06:08 +02:00
Claus 581eac5369 U4-10470 Missing transform for the scripts tree when upgrading 2017-09-27 10:04:07 +02:00
Sebastiaan Janssen 33cf983cae Bump version 2017-09-25 11:19:36 +02:00
Shannon 3ce6eb09aa Ensures HealthCheckController is authorized to user with developer access
(cherry picked from commit 7c4eb71f07)
2017-09-25 10:48:15 +02:00
Stephan 963015cf4a Fix LocalDb detection on 32/64 bits systems
(cherry picked from commit a239e92bc0)
2017-09-25 10:47:38 +02:00
Sebastiaan Janssen cc35af4eab Merge pull request #2209 from umbraco/temp-deploy-401
deploy-401 - fix id reservations
2017-09-25 10:46:05 +02:00
Stephan 349d89e4d7 deploy-401 - fix id reservations 2017-09-22 13:08:05 +02:00
Sebastiaan Janssen 1b3fc9fb21 Oops! Add -docs for all the log file pipes 2017-09-19 18:37:00 +02:00
Sebastiaan Janssen 2e98651d62 Create the out and tmp folders correctly 2017-09-19 18:16:19 +02:00
Sebastiaan Janssen 704e981284 Always do the node/bower/gulp build to get docs, start with node build first 2017-09-19 18:15:31 +02:00
Sebastiaan Janssen eda7f1e527 Bump version 2017-09-19 14:17:19 +02:00
Sebastiaan Janssen 018a066f86 U4-10428 Can't drag Image in Image Cropper (in Umbraco 7.6.7) 2017-09-19 14:10:20 +02:00
Sebastiaan Janssen e33a28ac19 Making sure we don't build Belle twice for the documentation 2017-09-19 11:38:20 +02:00
Shannon 0ef57d819c removes uneeded null checks from AuthenticationController for raising UserManager events 2017-09-19 13:31:43 +10:00
108 changed files with 1271 additions and 802 deletions
@@ -7,48 +7,40 @@ function Build-UmbracoDocs
$src = "$($uenv.SolutionRoot)\src"
$out = "$($uenv.SolutionRoot)\build.out"
$tmp = "$($uenv.SolutionRoot)\build.tmp"
$buildTemp = "$PSScriptRoot\temp"
$cache = 2
Prepare-Build -keep $uenv
################ Do the UI docs
# create Belle build folder, so that we don't cause a Belle rebuild
$belleBuildDir = "$src\Umbraco.Web.UI.Client\build"
if (-not (Test-Path $belleBuildDir))
{
mkdir $belleBuildDir > $null
}
Write-Host "Build UI documentation"
# get a temp clean node env (will restore)
Sandbox-Node $uenv
Write-Host "Executing gulp docs"
push-location "$($uenv.SolutionRoot)\src\Umbraco.Web.UI.Client"
write "node version is:" > $tmp\belle.log
&node -v >> $tmp\belle.log 2>&1
write "npm version is:" >> $tmp\belle.log 2>&1
&npm -v >> $tmp\belle.log 2>&1
write "cleaning npm cache" >> $tmp\belle.log 2>&1
&npm cache clean >> $tmp\belle.log 2>&1
write "installing bower" >> $tmp\belle.log 2>&1
&npm install -g bower >> $tmp\belle.log 2>&1
write "installing gulp" >> $tmp\belle.log 2>&1
&npm install -g gulp >> $tmp\belle.log 2>&1
write "installing gulp-cli" >> $tmp\belle.log 2>&1
&npm install -g gulp-cli --quiet >> $tmp\belle.log 2>&1
write "executing npm install" >> $tmp\belle.log 2>&1
&npm install >> $tmp\belle.log 2>&1
write "building docs using gulp" >> $tmp\belle.log 2>&1
&gulp docs >> $tmp\belle.log 2>&1
write "node version is:" > $tmp\belle-docs.log
&node -v >> $tmp\belle-docs.log 2>&1
write "npm version is:" >> $tmp\belle-docs.log 2>&1
&npm -v >> $tmp\belle-docs.log 2>&1
write "executing npm install" >> $tmp\belle-docs.log 2>&1
&npm install >> $tmp\belle-docs.log 2>&1
write "executing bower install" >> $tmp\belle-docs.log 2>&1
&npm install -g bower >> $tmp\belle-docs.log 2>&1
write "installing gulp" >> $tmp\belle-docs.log 2>&1
&npm install -g gulp >> $tmp\belle-docs.log 2>&1
write "installing gulp-cli" >> $tmp\belle-docs.log 2>&1
&npm install -g gulp-cli --quiet >> $tmp\belle-docs.log 2>&1
write "building docs using gulp" >> $tmp\belle-docs.log 2>&1
&gulp docs >> $tmp\belle-docs.log 2>&1
pop-location
Write-Host "Completed gulp docs build"
# fixme - should we filter the log to find errors?
#get-content .\build.tmp\belle-docs.log | %{ if ($_ -match "build") { write $_}}
# change baseUrl
$baseUrl = "https://our.umbraco.org/apidocs/ui/"
$indexPath = "$src/Umbraco.Web.UI.Client/docs/api/index.html"
@@ -40,6 +40,7 @@ function Get-UmbracoBuildEnv
&$nuget install 7-Zip.CommandLine -OutputDirectory $path -Verbosity quiet
$dir = ls "$path\7-Zip.CommandLine.*" | sort -property Name -descending | select -first 1
$file = ls -path "$dir" -name 7za.exe -recurse
$file = ls -path "$dir" -name 7za.exe -recurse | select -first 1 #A select is because there is tools\7za.exe & tools\x64\7za.exe
mv "$dir\$file" $sevenZip
Remove-Directory $dir
}
+11 -5
View File
@@ -51,13 +51,19 @@ function Prepare-Build
if (-not $keep)
{
Remove-Directory "$tmp"
mkdir "$tmp" > $null
Remove-Directory "$out"
mkdir "$out" > $null
Remove-Directory "$tmp"
Remove-Directory "$out"
}
if (-not (Test-Path "$tmp"))
{
mkdir "$tmp" > $null
}
if (-not (Test-Path "$out"))
{
mkdir "$out" > $null
}
# ensure proper web.config
$webUi = "$src\Umbraco.Web.UI"
Store-WebConfig $webUi
+3 -3
View File
@@ -34,9 +34,9 @@
<dependency id="ClientDependency-Mvc5" version="[1.8.0.0, 2.0.0)" />
<dependency id="AutoMapper" version="[3.3.1, 4.0.0)" />
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
<dependency id="Examine" version="[0.1.83, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.3, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.3, 5.0.0)" />
<dependency id="Examine" version="[0.1.88, 1.0.0)" />
<dependency id="ImageProcessor" version="[2.5.6, 3.0.0)" />
<dependency id="ImageProcessor.Web" version="[4.8.7, 5.0.0)" />
<dependency id="semver" version="[1.1.2, 3.0.0)" />
<!-- Markdown can not be updated due to: https://github.com/hey-red/markdownsharp/issues/71#issuecomment-233585487 -->
<dependency id="Markdown" version="[1.14.7, 2.0.0)" />
+2 -2
View File
@@ -17,9 +17,9 @@
<dependencies>
<dependency id="UmbracoCms.Core" version="[$version$]" />
<dependency id="Newtonsoft.Json" version="[10.0.2, 11.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.7, 4.0.0)" />
<dependency id="Umbraco.ModelsBuilder" version="[3.0.8, 4.0.0)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.1, 3.0.0)" />
<dependency id="ImageProcessor.Web.Config" version="[2.3.0, 3.0.0)" />
<dependency id="ImageProcessor.Web.Config" version="[2.3.1, 3.0.0)" />
</dependencies>
</metadata>
<files>
+1 -1
View File
@@ -43,7 +43,7 @@
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="settings" alias="scripts" title="Scripts" type="umbraco.loadScripts, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
<add application="settings" alias="scripts" title="Scripts" type="Umbraco.Web.Trees.ScriptTreeController, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="dictionary" title="Dictionary" type="umbraco.loadDictionary, umbraco" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="6"
+2 -2
View File
@@ -11,5 +11,5 @@ using System.Resources;
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("7.6.7")]
[assembly: AssemblyInformationalVersion("7.6.7")]
[assembly: AssemblyFileVersion("7.6.12")]
[assembly: AssemblyInformationalVersion("7.6.12")]
@@ -518,24 +518,41 @@ namespace Umbraco.Core.Configuration
internal static bool ContentCacheXmlStoredInCodeGen
{
get { return ContentCacheXmlStorageLocation == ContentXmlStorage.AspNetTemp; }
get { return LocalTempStorageLocation == LocalTempStorage.AspNetTemp; }
}
internal static ContentXmlStorage ContentCacheXmlStorageLocation
/// <summary>
/// This is the location type to store temporary files such as cache files or other localized files for a given machine
/// </summary>
/// <remarks>
/// Currently used for the xml cache file and the plugin cache files
/// </remarks>
internal static LocalTempStorage LocalTempStorageLocation
{
get
{
{
//there's a bunch of backwards compat config checks here....
//This is the current one
if (ConfigurationManager.AppSettings.ContainsKey("umbracoLocalTempStorage"))
{
return Enum<LocalTempStorage>.Parse(ConfigurationManager.AppSettings["umbracoLocalTempStorage"]);
}
//This one is old
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLStorage"))
{
return Enum<ContentXmlStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
}
return Enum<LocalTempStorage>.Parse(ConfigurationManager.AppSettings["umbracoContentXMLStorage"]);
}
//This one is older
if (ConfigurationManager.AppSettings.ContainsKey("umbracoContentXMLUseLocalTemp"))
{
return bool.Parse(ConfigurationManager.AppSettings["umbracoContentXMLUseLocalTemp"])
? ContentXmlStorage.AspNetTemp
: ContentXmlStorage.Default;
? LocalTempStorage.AspNetTemp
: LocalTempStorage.Default;
}
return ContentXmlStorage.Default;
return LocalTempStorage.Default;
}
}
@@ -1,6 +1,6 @@
namespace Umbraco.Core.Configuration
{
internal enum ContentXmlStorage
internal enum LocalTempStorage
{
Default,
AspNetTemp,
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration
{
public class UmbracoVersion
{
private static readonly Version Version = new Version("7.6.7");
private static readonly Version Version = new Version("7.6.12");
/// <summary>
/// Gets the current version of Umbraco.
+4 -4
View File
@@ -73,11 +73,11 @@ namespace Umbraco.Core.IO
{
get
{
switch (GlobalSettings.ContentCacheXmlStorageLocation)
switch (GlobalSettings.LocalTempStorageLocation)
{
case ContentXmlStorage.AspNetTemp:
case LocalTempStorage.AspNetTemp:
return Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco.config");
case ContentXmlStorage.EnvironmentTemp:
case LocalTempStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoXml",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
@@ -85,7 +85,7 @@ namespace Umbraco.Core.IO
// utilizing an old path
appDomainHash);
return Path.Combine(cachePath, "umbraco.config");
case ContentXmlStorage.Default:
case LocalTempStorage.Default:
return IOHelper.ReturnPath("umbracoContentXML", "~/App_Data/umbraco.config");
default:
throw new ArgumentOutOfRangeException();
+22 -9
View File
@@ -24,6 +24,7 @@ namespace Umbraco.Core.Persistence
{
private int _version;
private bool _hasVersion;
private string _exe;
#region Availability & Version
@@ -84,16 +85,31 @@ namespace Umbraco.Core.Persistence
{
_hasVersion = true;
_version = -1;
_exe = null;
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
if (programFiles == null) return;
// MS SQL Server installs in e.g. "C:\Program Files\Microsoft SQL Server", so
// we want to detect it in "%ProgramFiles%\Microsoft SQL Server" - however, if
// Umbraco runs as a 32bits process (e.g. IISExpress configured as 32bits)
// on a 64bits system, %ProgramFiles% will point to "C:\Program Files (x86)"
// and SQL Server cannot be found. But then, %ProgramW6432% will point to
// the original "C:\Program Files". Using it to fix the path.
// see also: MSDN doc for WOW64 implementation
//
var programW6432 = Environment.GetEnvironmentVariable("ProgramW6432");
if (string.IsNullOrWhiteSpace(programW6432) == false && programW6432 != programFiles)
programFiles = programW6432;
if (string.IsNullOrWhiteSpace(programFiles)) return;
// detect 14, 13, 12, 11
for (var i = 14; i > 10; i--)
{
var path = Path.Combine(programFiles, string.Format(@"Microsoft SQL Server\{0}0\Tools\Binn\SqlLocalDB.exe", i));
if (File.Exists(path) == false) continue;
var exe = Path.Combine(programFiles, string.Format(@"Microsoft SQL Server\{0}0\Tools\Binn\SqlLocalDB.exe", i));
if (File.Exists(exe) == false) continue;
_version = i;
_exe = exe;
break;
}
}
@@ -897,16 +913,13 @@ namespace Umbraco.Core.Persistence
/// </remarks>
private int ExecuteSqlLocalDb(string args, out string output, out string error)
{
var programFiles = Environment.GetEnvironmentVariable("ProgramFiles");
if (programFiles == null)
if (_exe == null) // should never happen - we should not execute if not available
{
output = string.Empty;
error = "SqlLocalDB.exe not found";
return -1;
}
var path = Path.Combine(programFiles, string.Format(@"Microsoft SQL Server\{0}0\Tools\Binn\SqlLocalDB.exe", _version));
var p = new Process
{
StartInfo =
@@ -914,7 +927,7 @@ namespace Umbraco.Core.Persistence
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = path,
FileName = _exe,
Arguments = args,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
@@ -929,7 +942,7 @@ namespace Umbraco.Core.Persistence
}
/// <summary>
/// Returns a Unicode string with the delimiters added to make the input string a valid SQL Server delimited identifier.
/// Returns a Unicode string with the delimiters added to make the input string a valid SQL Server delimited identifier.
/// </summary>
/// <param name="name">The name to quote.</param>
/// <param name="quote">A quote character.</param>
@@ -133,7 +133,9 @@ namespace Umbraco.Core.Persistence.Repositories
.InnerJoin<ContentDto>(SqlSyntax)
.On<ContentVersionDto, ContentDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<NodeDto>(SqlSyntax)
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId);
.On<ContentDto, NodeDto>(SqlSyntax, left => left.NodeId, right => right.NodeId)
.InnerJoin<ContentTypeDto>()
.On<ContentTypeDto, ContentDto>(left => left.NodeId, right => right.ContentTypeId);
//TODO: IF we want to enable querying on content type information this will need to be joined
//.InnerJoin<ContentTypeDto>(SqlSyntax)
//.On<ContentDto, ContentTypeDto>(SqlSyntax, left => left.ContentTypeId, right => right.NodeId, SqlSyntax);
@@ -837,11 +839,21 @@ order by umbracoNode.{2}, umbracoNode.parentID, umbracoNode.sortOrder",
}
public int CountPublished()
{
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
.Where<DocumentDto>(x => x.Published == true);
return Database.ExecuteScalar<int>(sql);
public int CountPublished(string contentTypeAlias = null)
{
if (contentTypeAlias.IsNullOrWhiteSpace())
{
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
.Where<DocumentDto>(x => x.Published == true);
return Database.ExecuteScalar<int>(sql);
}
else
{
var sql = GetBaseQuery(true).Where<NodeDto>(x => x.Trashed == false)
.Where<DocumentDto>(x => x.Published == true)
.Where<ContentTypeDto>(x => x.Alias == contentTypeAlias);
return Database.ExecuteScalar<int>(sql);
}
}
public void ReplaceContentPermissions(EntityPermissionSet permissionSet)
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Persistence.Repositories
/// <remarks>
/// We require this on the repo because the IQuery{IContent} cannot supply the 'newest' parameter
/// </remarks>
int CountPublished();
int CountPublished(string contentTypeAlias = null);
/// <summary>
/// Used to bulk update the permissions set for a content item. This will replace all permissions
@@ -705,8 +705,8 @@ ORDER BY contentNodeId, versionId, propertytypeid
case "NAME":
return "umbracoNode.text";
case "PUBLISHED":
case "OWNER":
return "cmsDocument.published";
case "OWNER":
//TODO: This isn't going to work very nicely because it's going to order by ID, not by letter
return "umbracoNode.nodeUser";
// Members only
+123 -82
View File
@@ -6,6 +6,7 @@ using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Web;
using System.Web.Compilation;
using Umbraco.Core.Cache;
using Umbraco.Core.IO;
@@ -15,6 +16,7 @@ using Umbraco.Core.Persistence.SqlSyntax;
using Umbraco.Core.Profiling;
using Umbraco.Core.PropertyEditors;
using umbraco.interfaces;
using Umbraco.Core.Configuration;
using File = System.IO.File;
namespace Umbraco.Core
@@ -39,7 +41,8 @@ namespace Umbraco.Core
private readonly IServiceProvider _serviceProvider;
private readonly IRuntimeCacheProvider _runtimeCache;
private readonly ProfilingLogger _logger;
private readonly string _tempFolder;
private readonly Lazy<string> _pluginListFilePath = new Lazy<string>(GetPluginListFilePath);
private readonly Lazy<string> _pluginHashFilePath = new Lazy<string>(GetPluginHashFilePath);
private readonly object _typesLock = new object();
private readonly Dictionary<TypeListKey, TypeList> _types = new Dictionary<TypeListKey, TypeList>();
@@ -64,15 +67,8 @@ namespace Umbraco.Core
_serviceProvider = serviceProvider;
_runtimeCache = runtimeCache;
_logger = logger;
// the temp folder where the cache file lives
_tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
if (Directory.Exists(_tempFolder) == false)
Directory.CreateDirectory(_tempFolder);
var pluginListFile = GetPluginListFilePath();
_logger = logger;
if (detectChanges)
{
//first check if the cached hash is string.Empty, if it is then we need
@@ -84,7 +80,8 @@ namespace Umbraco.Core
// if the hash has changed, clear out the persisted list no matter what, this will force
// rescanning of all plugin types including lazy ones.
// http://issues.umbraco.org/issue/U4-4789
File.Delete(pluginListFile);
if (File.Exists(_pluginListFilePath.Value))
File.Delete(_pluginListFilePath.Value);
WriteCachePluginsHash();
}
@@ -94,7 +91,8 @@ namespace Umbraco.Core
// if the hash has changed, clear out the persisted list no matter what, this will force
// rescanning of all plugin types including lazy ones.
// http://issues.umbraco.org/issue/U4-4789
File.Delete(pluginListFile);
if (File.Exists(_pluginListFilePath.Value))
File.Delete(_pluginListFilePath.Value);
// always set to true if we're not detecting (generally only for testing)
RequiresRescanning = true;
@@ -186,12 +184,11 @@ namespace Umbraco.Core
get
{
if (_cachedAssembliesHash != null)
return _cachedAssembliesHash;
return _cachedAssembliesHash;
if (File.Exists(_pluginHashFilePath.Value) == false) return string.Empty;
var filePath = GetPluginHashFilePath();
if (File.Exists(filePath) == false) return string.Empty;
var hash = File.ReadAllText(filePath, Encoding.UTF8);
var hash = File.ReadAllText(_pluginHashFilePath.Value, Encoding.UTF8);
_cachedAssembliesHash = hash;
return _cachedAssembliesHash;
@@ -209,8 +206,8 @@ namespace Umbraco.Core
if (_currentAssembliesHash != null)
return _currentAssembliesHash;
_currentAssembliesHash = GetFileHash(new List<Tuple<FileSystemInfo, bool>>
{
_currentAssembliesHash = GetFileHash(new List<Tuple<FileSystemInfo, bool>>
{
// the bin folder and everything in it
new Tuple<FileSystemInfo, bool>(new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Bin)), false),
// the app code folder and everything in it
@@ -218,8 +215,8 @@ namespace Umbraco.Core
// global.asax (the app domain also monitors this, if it changes will do a full restart)
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath("~/global.asax")), false),
// trees.config - use the contents to create the hash since this gets resaved on every app startup!
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true)
}, _logger);
new Tuple<FileSystemInfo, bool>(new FileInfo(IOHelper.MapPath(SystemDirectories.Config + "/trees.config")), true)
}, _logger);
return _currentAssembliesHash;
}
@@ -229,9 +226,8 @@ namespace Umbraco.Core
/// Writes the assembly hash file.
/// </summary>
private void WriteCachePluginsHash()
{
var filePath = GetPluginHashFilePath();
File.WriteAllText(filePath, CurrentAssembliesHash.ToString(), Encoding.UTF8);
{
File.WriteAllText(_pluginHashFilePath.Value, CurrentAssembliesHash, Encoding.UTF8);
}
/// <summary>
@@ -274,7 +270,7 @@ namespace Umbraco.Core
}
}
}
return generator.GenerateHash();
return generator.GenerateHash();
}
}
}
@@ -314,7 +310,7 @@ namespace Umbraco.Core
uniqInfos.Add(fileOrFolder.FullName);
generator.AddFileSystemItem(fileOrFolder);
}
return generator.GenerateHash();
return generator.GenerateHash();
}
}
}
@@ -347,12 +343,11 @@ namespace Umbraco.Core
{
return ReadCache();
}
catch
catch (Exception ex)
{
try
{
var filePath = GetPluginListFilePath();
File.Delete(filePath);
File.Delete(_pluginListFilePath.Value);
}
catch
{
@@ -365,13 +360,12 @@ namespace Umbraco.Core
internal Dictionary<Tuple<string, string>, IEnumerable<string>> ReadCache()
{
var cache = new Dictionary<Tuple<string, string>, IEnumerable<string>>();
var filePath = GetPluginListFilePath();
if (File.Exists(filePath) == false)
var cache = new Dictionary<Tuple<string, string>, IEnumerable<string>>();
if (File.Exists(_pluginListFilePath.Value) == false)
return cache;
using (var stream = GetFileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout))
using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Open, FileAccess.Read, FileShare.Read, ListFileOpenReadTimeout))
using (var reader = new StreamReader(stream))
{
while (true)
@@ -414,38 +408,86 @@ namespace Umbraco.Core
/// <remarks>Generally only used for resetting cache, for example during the install process.</remarks>
public void ClearPluginCache()
{
var path = GetPluginListFilePath();
if (File.Exists(path))
File.Delete(path);
path = GetPluginHashFilePath();
if (File.Exists(path))
File.Delete(path);
if (File.Exists(_pluginListFilePath.Value))
File.Delete(_pluginListFilePath.Value);
if (File.Exists(_pluginHashFilePath.Value))
File.Delete(_pluginHashFilePath.Value);
_runtimeCache.ClearCacheItem(CacheKey);
}
private string GetPluginListFilePath()
private static string GetPluginListFilePath()
{
var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list";
return Path.Combine(_tempFolder, filename);
string pluginListFilePath;
switch (GlobalSettings.LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
pluginListFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.list");
break;
case LocalTempStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
appDomainHash);
pluginListFilePath = Path.Combine(cachePath, "umbraco-plugins.list");
break;
case LocalTempStorage.Default:
default:
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
pluginListFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".list");
break;
}
//ensure the folder exists
var folder = Path.GetDirectoryName(pluginListFilePath);
if (folder == null)
throw new InvalidOperationException("The folder could not be determined for the file " + pluginListFilePath);
if (Directory.Exists(folder) == false)
Directory.CreateDirectory(folder);
return pluginListFilePath;
}
private string GetPluginHashFilePath()
private static string GetPluginHashFilePath()
{
var filename = "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash";
return Path.Combine(_tempFolder, filename);
string pluginHashFilePath;
switch (GlobalSettings.LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
pluginHashFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData\umbraco-plugins.hash");
break;
case LocalTempStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
appDomainHash);
pluginHashFilePath = Path.Combine(cachePath, "umbraco-plugins.hash");
break;
case LocalTempStorage.Default:
default:
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/PluginCache");
pluginHashFilePath = Path.Combine(tempFolder, "umbraco-plugins." + NetworkHelper.FileSafeMachineName + ".hash");
break;
}
//ensure the folder exists
var folder = Path.GetDirectoryName(pluginHashFilePath);
if (folder == null)
throw new InvalidOperationException("The folder could not be determined for the file " + pluginHashFilePath);
if (Directory.Exists(folder) == false)
Directory.CreateDirectory(folder);
return pluginHashFilePath;
}
internal void WriteCache()
{
// be absolutely sure
if (Directory.Exists(_tempFolder) == false)
Directory.CreateDirectory(_tempFolder);
var filePath = GetPluginListFilePath();
using (var stream = GetFileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout))
using (var stream = GetFileStream(_pluginListFilePath.Value, FileMode.Create, FileAccess.Write, FileShare.None, ListFileOpenWriteTimeout))
using (var writer = new StreamWriter(stream))
{
foreach (var typeList in _types.Values)
@@ -461,8 +503,7 @@ namespace Umbraco.Core
internal void UpdateCache()
{
// note
// at the moment we write the cache to disk every time we update it. ideally we defer the writing
// TODO: at the moment we write the cache to disk every time we update it. ideally we defer the writing
// since all the updates are going to happen in a row when Umbraco starts. that being said, the
// file is small enough, so it is not a priority.
WriteCache();
@@ -475,13 +516,13 @@ namespace Umbraco.Core
while (true)
{
try
{
{
return new FileStream(path, fileMode, fileAccess, fileShare);
}
catch
catch (Exception ex)
{
if (--attempts == 0)
throw;
throw;
LogHelper.Debug<PluginManager>(string.Format("Attempted to get filestream for file {0} failed, {1} attempts left, pausing for {2} milliseconds", path, attempts, pauseMilliseconds));
Thread.Sleep(pauseMilliseconds);
@@ -560,7 +601,7 @@ namespace Umbraco.Core
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
{
return ResolveTypesInternal(
typeof (T), null,
typeof(T), null,
() => TypeFinder.FindClassesOfType<T>(specificAssemblies ?? AssembliesToScan),
cache);
}
@@ -569,14 +610,14 @@ namespace Umbraco.Core
// filter the cached discovered types (and cache the result)
var discovered = ResolveTypesInternal(
typeof (IDiscoverable), null,
typeof(IDiscoverable), null,
() => TypeFinder.FindClassesOfType<IDiscoverable>(AssembliesToScan),
true);
return ResolveTypesInternal(
typeof (T), null,
typeof(T), null,
() => discovered
.Where(x => typeof (T).IsAssignableFrom(x)),
.Where(x => typeof(T).IsAssignableFrom(x)),
true);
}
@@ -599,7 +640,7 @@ namespace Umbraco.Core
if (cache == false || typeof(IDiscoverable).IsAssignableFrom(typeof(T)) == false)
{
return ResolveTypesInternal(
typeof (T), typeof (TAttribute),
typeof(T), typeof(TAttribute),
() => TypeFinder.FindClassesOfTypeWithAttribute<T, TAttribute>(specificAssemblies ?? AssembliesToScan),
cache);
}
@@ -608,12 +649,12 @@ namespace Umbraco.Core
// filter the cached discovered types (and cache the result)
var discovered = ResolveTypesInternal(
typeof (IDiscoverable), null,
typeof(IDiscoverable), null,
() => TypeFinder.FindClassesOfType<IDiscoverable>(AssembliesToScan),
true);
return ResolveTypesInternal(
typeof (T), typeof (TAttribute),
typeof(T), typeof(TAttribute),
() => discovered
.Where(x => typeof(T).IsAssignableFrom(x))
.Where(x => x.GetCustomAttributes<TAttribute>(false).Any()),
@@ -635,7 +676,7 @@ namespace Umbraco.Core
cache &= specificAssemblies == null;
return ResolveTypesInternal(
typeof (object), typeof (TAttribute),
typeof(object), typeof(TAttribute),
() => TypeFinder.FindClassesWithAttribute<TAttribute>(specificAssemblies ?? AssembliesToScan),
cache);
}
@@ -652,14 +693,14 @@ namespace Umbraco.Core
var name = ResolvedName(baseType, attributeType);
lock (_typesLock)
using (_logger.TraceDuration<PluginManager>(
"Resolving " + name,
"Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find
{
// resolve within a lock & timer
return ResolveTypesInternalLocked(baseType, attributeType, finder, cache);
}
lock (_typesLock)
using (_logger.TraceDuration<PluginManager>(
"Resolving " + name,
"Resolved " + name)) // cannot contain typesFound.Count as it's evaluated before the find
{
// resolve within a lock & timer
return ResolveTypesInternalLocked(baseType, attributeType, finder, cache);
}
}
private static string ResolvedName(Type baseType, Type attributeType)
@@ -691,7 +732,7 @@ namespace Umbraco.Core
// else proceed,
typeList = new TypeList(baseType, attributeType);
var scan = RequiresRescanning || File.Exists(GetPluginListFilePath()) == false;
var scan = RequiresRescanning || File.Exists(_pluginListFilePath.Value) == false;
if (scan)
{
@@ -795,7 +836,7 @@ namespace Umbraco.Core
public TypeListKey(Type baseType, Type attributeType)
{
BaseType = baseType ?? typeof (object);
BaseType = baseType ?? typeof(object);
AttributeType = attributeType;
}
@@ -813,7 +854,7 @@ namespace Umbraco.Core
var hash = 5381;
hash = ((hash << 5) + hash) ^ BaseType.GetHashCode();
hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof (TypeListKey)).GetHashCode();
hash = ((hash << 5) + hash) ^ (AttributeType ?? typeof(TypeListKey)).GetHashCode();
return hash;
}
}
@@ -876,7 +917,7 @@ namespace Umbraco.Core
{
// look for IParameterEditor (fast, IDiscoverable) then filter
var propertyEditor = typeof (PropertyEditor);
var propertyEditor = typeof(PropertyEditor);
return mgr.ResolveTypes<IParameterEditor>()
.Where(x => propertyEditor.IsAssignableFrom(x) && x != propertyEditor);
@@ -891,8 +932,8 @@ namespace Umbraco.Core
/// </remarks>
public static IEnumerable<Type> ResolveParameterEditors(this PluginManager mgr)
{
var propertyEditor = typeof (PropertyEditor);
var parameterEditor = typeof (ParameterEditor);
var propertyEditor = typeof(PropertyEditor);
var parameterEditor = typeof(ParameterEditor);
return mgr.ResolveTypes<IParameterEditor>()
.Where(x => x != propertyEditor && x != parameterEditor);
@@ -974,4 +1015,4 @@ namespace Umbraco.Core
return mgr.ResolveTypesWithAttribute<ISqlSyntaxProvider, SqlSyntaxProviderAttribute>();
}
}
}
}
@@ -12,6 +12,7 @@ using Umbraco.Core.Models.Identity;
namespace Umbraco.Core.Security
{
//TODO: In v8 we need to change this to use an int? nullable TKey instead, see notes against overridden TwoFactorSignInAsync
public class BackOfficeSignInManager : SignInManager<BackOfficeIdentityUser, int>
{
private readonly ILogger _logger;
@@ -250,5 +251,67 @@ namespace Umbraco.Core.Security
}
return null;
}
/// <summary>
/// Two factor verification step
/// </summary>
/// <param name="provider"></param>
/// <param name="code"></param>
/// <param name="isPersistent"></param>
/// <param name="rememberBrowser"></param>
/// <returns></returns>
/// <remarks>
/// This is implemented because we cannot override GetVerifiedUserIdAsync and instead we have to shadow it
/// so due to this and because we are using an INT as the TKey and not an object, it can never be null. Adding to that
/// the default(int) value returned by the base class is always a valid user (i.e. the admin) so we just have to duplicate
/// all of this code to check for -1 instead.
/// </remarks>
public override async Task<SignInStatus> TwoFactorSignInAsync(string provider, string code, bool isPersistent, bool rememberBrowser)
{
var userId = await GetVerifiedUserIdAsync();
if (userId == -1)
{
return SignInStatus.Failure;
}
var user = await UserManager.FindByIdAsync(userId);
if (user == null)
{
return SignInStatus.Failure;
}
if (await UserManager.IsLockedOutAsync(user.Id))
{
return SignInStatus.LockedOut;
}
if (await UserManager.VerifyTwoFactorTokenAsync(user.Id, provider, code))
{
// When token is verified correctly, clear the access failed count used for lockout
await UserManager.ResetAccessFailedCountAsync(user.Id);
await SignInAsync(user, isPersistent, rememberBrowser);
return SignInStatus.Success;
}
// If the token is incorrect, record the failure which also may cause the user to be locked out
await UserManager.AccessFailedAsync(user.Id);
return SignInStatus.Failure;
}
/// <summary>Send a two factor code to a user</summary>
/// <param name="provider"></param>
/// <returns></returns>
/// <remarks>
/// This is implemented because we cannot override GetVerifiedUserIdAsync and instead we have to shadow it
/// so due to this and because we are using an INT as the TKey and not an object, it can never be null. Adding to that
/// the default(int) value returned by the base class is always a valid user (i.e. the admin) so we just have to duplicate
/// all of this code to check for -1 instead.
/// </remarks>
public override async Task<bool> SendTwoFactorCodeAsync(string provider)
{
var userId = await GetVerifiedUserIdAsync();
if (userId == -1)
return false;
var token = await UserManager.GenerateTwoFactorTokenAsync(userId, provider);
var identityResult = await UserManager.NotifyTwoFactorTokenAsync(userId, provider, token);
return identityResult.Succeeded;
}
}
}
@@ -37,11 +37,19 @@ namespace Umbraco.Core.Security
var identity = Thread.CurrentPrincipal.GetUmbracoIdentity();
if (identity != null)
{
var user = userService.GetByUsername(identity.Username);
var userIsAdmin = user.IsAdmin();
if (userIsAdmin)
//get the user id from the identity
var userId = 0;
if(int.TryParse(identity.Id.ToString(), out userId))
{
canReset = true;
var user = userService.GetUserById(userId);
if (user != null)
{
var userIsAdmin = user.IsAdmin();
if (userIsAdmin)
{
canReset = true;
}
}
}
}
}
+93 -3
View File
@@ -65,7 +65,7 @@ namespace Umbraco.Core.Services
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{
var repository = RepositoryFactory.CreateContentRepository(uow);
return repository.CountPublished();
return repository.CountPublished(contentTypeAlias);
}
}
@@ -1784,7 +1784,7 @@ namespace Umbraco.Core.Services
{
var asArray = items.ToArray();
var saveEventArgs = new SaveEventArgs<IContent>(asArray);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs))
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs, "Saving"))
{
uow.Commit();
return false;
@@ -1831,7 +1831,97 @@ namespace Umbraco.Core.Services
if (raiseEvents)
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs);
uow.Events.Dispatch(Saved, this, saveEventArgs, "Saved");
}
if (shouldBePublished.Any())
{
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
_publishingStrategy.PublishingFinalized(uow, shouldBePublished, false);
}
Audit(uow, AuditType.Sort, "Sorting content performed by user", userId, 0);
uow.Commit();
}
}
return true;
}
/// <summary>
/// Sorts a collection of <see cref="IContent"/> objects by updating the SortOrder according
/// to the ordering of node Ids passed in.
/// </summary>
/// <remarks>
/// Using this method will ensure that the Published-state is maintained upon sorting
/// so the cache is updated accordingly - as needed.
/// </remarks>
/// <param name="ids"></param>
/// <param name="userId"></param>
/// <param name="raiseEvents"></param>
/// <returns>True if sorting succeeded, otherwise False</returns>
public bool Sort(int[] ids, int userId = 0, bool raiseEvents = true)
{
var shouldBePublished = new List<IContent>();
var shouldBeSaved = new List<IContent>();
using (new WriteLock(Locker))
{
var allContent = GetByIds(ids).ToDictionary(x => x.Id, x => x);
var items = ids.Select(x => allContent[x]);
using (var uow = UowProvider.GetUnitOfWork())
{
var asArray = items.ToArray();
var saveEventArgs = new SaveEventArgs<IContent>(asArray);
if (raiseEvents && uow.Events.DispatchCancelable(Saving, this, saveEventArgs, "Saving"))
{
uow.Commit();
return false;
}
var repository = RepositoryFactory.CreateContentRepository(uow);
var i = 0;
foreach (var content in asArray)
{
//If the current sort order equals that of the content
//we don't need to update it, so just increment the sort order
//and continue.
if (content.SortOrder == i)
{
i++;
continue;
}
content.SortOrder = i;
content.WriterId = userId;
i++;
if (content.Published)
{
//TODO: This should not be an inner operation, but if we do this, it cannot raise events and cannot be cancellable!
var published = _publishingStrategy.Publish(uow, content, userId).Success;
shouldBePublished.Add(content);
}
else
shouldBeSaved.Add(content);
repository.AddOrUpdate(content);
//add or update a preview
repository.AddOrUpdatePreviewXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
}
foreach (var content in shouldBePublished)
{
//Create and Save ContentXml DTO
repository.AddOrUpdateContentXml(content, c => _entitySerializer.Serialize(this, _dataTypeService, _userService, c));
}
if (raiseEvents)
{
saveEventArgs.CanCancel = false;
uow.Events.Dispatch(Saved, this, saveEventArgs, "Saved");
}
if (shouldBePublished.Any())
+15 -1
View File
@@ -618,7 +618,21 @@ namespace Umbraco.Core.Services
/// <param name="userId"></param>
/// <param name="raiseEvents"></param>
/// <returns>True if sorting succeeded, otherwise False</returns>
bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true);
bool Sort(IEnumerable<IContent> items, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Sorts a collection of <see cref="IContent"/> objects by updating the SortOrder according
/// to the ordering of node Ids passed in.
/// </summary>
/// <remarks>
/// Using this method will ensure that the Published-state is maintained upon sorting
/// so the cache is updated accordingly - as needed.
/// </remarks>
/// <param name="ids"></param>
/// <param name="userId"></param>
/// <param name="raiseEvents"></param>
/// <returns>True if sorting succeeded, otherwise False</returns>
bool Sort(int[] ids, int userId = 0, bool raiseEvents = true);
/// <summary>
/// Gets the parent of the current content as an <see cref="IContent"/> item.
+8 -8
View File
@@ -10,7 +10,7 @@ namespace Umbraco.Core.Services
{
private readonly IDatabaseUnitOfWorkProvider _uowProvider;
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim();
private readonly Dictionary<int, TypedId<Guid>> _id2Key = new Dictionary<int, TypedId<Guid>>();
private readonly Dictionary<Guid, TypedId<int>> _key2Id = new Dictionary<Guid, TypedId<int>>();
@@ -39,8 +39,8 @@ namespace Umbraco.Core.Services
int? val;
using (var uow = _uowProvider.GetUnitOfWork())
{
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id AND nodeObjectType=@nodeObjectType",
new { id = key, nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType) });
val = uow.Database.ExecuteScalar<int?>("SELECT id FROM umbracoNode WHERE uniqueId=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
new { id = key, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
uow.Commit();
}
@@ -92,8 +92,8 @@ namespace Umbraco.Core.Services
Guid? val;
using (var uow = _uowProvider.GetUnitOfWork())
{
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id AND nodeObjectType=@nodeObjectType",
new { id, nodeObjectType = GetNodeObjectTypeGuid(umbracoObjectType) });
val = uow.Database.ExecuteScalar<Guid?>("SELECT uniqueId FROM umbracoNode WHERE id=@id AND (nodeObjectType=@type OR nodeObjectType=@reservation)",
new { id, type = GetNodeObjectTypeGuid(umbracoObjectType), reservation = Constants.ObjectTypes.IdReservationGuid });
uow.Commit();
}
@@ -106,8 +106,8 @@ namespace Umbraco.Core.Services
try
{
_locker.EnterWriteLock();
_id2Key[id] = new TypedId<Guid>(val.Value, umbracoObjectType); ;
_key2Id[val.Value] = new TypedId<int>();
_id2Key[id] = new TypedId<Guid>(val.Value, umbracoObjectType);
_key2Id[val.Value] = new TypedId<int>(id, umbracoObjectType);
}
finally
{
@@ -179,7 +179,7 @@ namespace Umbraco.Core.Services
{
private readonly T _id;
private readonly UmbracoObjectTypes _umbracoObjectType;
public T Id
{
get { return _id; }
@@ -14,6 +14,7 @@ using Umbraco.Core.Logging;
using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence;
using umbraco.interfaces;
using Umbraco.Core.Configuration;
using Umbraco.Core.Persistence.SqlSyntax;
namespace Umbraco.Core.Sync
@@ -39,6 +40,7 @@ namespace Umbraco.Core.Sync
private bool _syncing;
private bool _released;
private readonly ProfilingLogger _profilingLogger;
private readonly Lazy<string> _distCacheFilePath = new Lazy<string>(GetDistCacheFilePath);
protected DatabaseServerMessengerOptions Options { get; private set; }
protected ApplicationContext ApplicationContext { get { return _appContext; } }
@@ -460,10 +462,9 @@ namespace Umbraco.Core.Sync
/// </remarks>
private void ReadLastSynced()
{
var path = SyncFilePath;
if (File.Exists(path) == false) return;
if (File.Exists(_distCacheFilePath.Value) == false) return;
var content = File.ReadAllText(path);
var content = File.ReadAllText(_distCacheFilePath.Value);
int last;
if (int.TryParse(content, out last))
_lastId = last;
@@ -478,7 +479,7 @@ namespace Umbraco.Core.Sync
/// </remarks>
private void SaveLastSynced(int id)
{
File.WriteAllText(SyncFilePath, id.ToString(CultureInfo.InvariantCulture));
File.WriteAllText(_distCacheFilePath.Value, id.ToString(CultureInfo.InvariantCulture));
_lastId = id;
}
@@ -498,20 +499,40 @@ namespace Umbraco.Core.Sync
+ "/D" + AppDomain.CurrentDomain.Id // eg 22
+ "] " + Guid.NewGuid().ToString("N").ToUpper(); // make it truly unique
/// <summary>
/// Gets the sync file path for the local server.
/// </summary>
/// <returns>The sync file path for the local server.</returns>
private static string SyncFilePath
private static string GetDistCacheFilePath()
{
get
{
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache/" + NetworkHelper.FileSafeMachineName);
if (Directory.Exists(tempFolder) == false)
Directory.CreateDirectory(tempFolder);
var fileName = HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt";
return Path.Combine(tempFolder, HttpRuntime.AppDomainAppId.ReplaceNonAlphanumericChars(string.Empty) + "-lastsynced.txt");
string distCacheFilePath;
switch (GlobalSettings.LocalTempStorageLocation)
{
case LocalTempStorage.AspNetTemp:
distCacheFilePath = Path.Combine(HttpRuntime.CodegenDir, @"UmbracoData", fileName);
break;
case LocalTempStorage.EnvironmentTemp:
var appDomainHash = HttpRuntime.AppDomainAppId.ToSHA1();
var cachePath = Path.Combine(Environment.ExpandEnvironmentVariables("%temp%"), "UmbracoData",
//include the appdomain hash is just a safety check, for example if a website is moved from worker A to worker B and then back
// to worker A again, in theory the %temp% folder should already be empty but we really want to make sure that its not
// utilizing an old path
appDomainHash);
distCacheFilePath = Path.Combine(cachePath, fileName);
break;
case LocalTempStorage.Default:
default:
var tempFolder = IOHelper.MapPath("~/App_Data/TEMP/DistCache");
distCacheFilePath = Path.Combine(tempFolder, fileName);
break;
}
//ensure the folder exists
var folder = Path.GetDirectoryName(distCacheFilePath);
if (folder == null)
throw new InvalidOperationException("The folder could not be determined for the file " + distCacheFilePath);
if (Directory.Exists(folder) == false)
Directory.CreateDirectory(folder);
return distCacheFilePath;
}
#endregion
+3 -3
View File
@@ -56,8 +56,8 @@
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ImageProcessor, Version=2.5.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.3\lib\net45\ImageProcessor.dll</HintPath>
<Reference Include="ImageProcessor, Version=2.5.6.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.6\lib\net45\ImageProcessor.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
@@ -200,7 +200,7 @@
<Compile Include="Configuration\BaseRest\ExtensionElement.cs" />
<Compile Include="Configuration\BaseRest\ExtensionElementCollection.cs" />
<Compile Include="Configuration\BaseRest\MethodElement.cs" />
<Compile Include="Configuration\ContentXmlStorage.cs" />
<Compile Include="Configuration\LocalTempStorage.cs" />
<Compile Include="Configuration\CoreDebug.cs" />
<Compile Include="Configuration\Dashboard\AccessElement.cs" />
<Compile Include="Configuration\Dashboard\AccessItem.cs" />
+1 -1
View File
@@ -3,7 +3,7 @@
<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="ImageProcessor" version="2.5.3" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.6" targetFramework="net45" />
<package id="log4net" version="2.0.8" targetFramework="net45" />
<package id="Log4Net.Async" version="2.0.4" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
+1 -1
View File
@@ -20,7 +20,7 @@ using Umbraco.Tests.TestHelpers.Entities;
namespace Umbraco.Tests.Models
{
[TestFixture]
public class ContentTests : BaseUmbracoConfigurationTest
public class ContentTests : BaseUmbracoApplicationTest
{
[SetUp]
public void Init()
@@ -26,15 +26,15 @@ namespace Umbraco.Tests.Services
public override void TearDown()
{
base.TearDown();
}
}
[Test]
public void EntityService_Can_Get_Paged_Content_Children()
{
var contentType = ServiceContext.ContentTypeService.GetContentType("umbTextpage");
var root = MockedContent.CreateSimpleContent(contentType);
var root = MockedContent.CreateSimpleContent(contentType);
ServiceContext.ContentService.Save(root);
for (int i = 0; i < 10; i++)
{
@@ -45,7 +45,7 @@ namespace Umbraco.Tests.Services
var service = ServiceContext.EntityService;
long total;
var entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Document, 0, 6, out total).ToArray();
var entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Document, 0, 6, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(6));
Assert.That(total, Is.EqualTo(10));
entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Document, 1, 6, out total).ToArray();
@@ -72,7 +72,7 @@ namespace Umbraco.Tests.Services
var c2 = MockedContent.CreateSimpleContent(contentType, Guid.NewGuid().ToString(), c1);
ServiceContext.ContentService.Save(c2);
count++;
}
}
}
var service = ServiceContext.EntityService;
@@ -181,7 +181,7 @@ namespace Umbraco.Tests.Services
var root = MockedContent.CreateSimpleContent(contentType);
ServiceContext.ContentService.Save(root);
for (int i = 0; i < 10; i++)
{
var c1 = MockedContent.CreateSimpleContent(contentType, "ssss" + Guid.NewGuid(), root);
@@ -204,7 +204,7 @@ namespace Umbraco.Tests.Services
Assert.That(entities.Length, Is.EqualTo(50));
Assert.That(total, Is.EqualTo(50));
}
[Test]
public void EntityService_Can_Get_Paged_Media_Children()
{
@@ -227,7 +227,7 @@ namespace Umbraco.Tests.Services
Assert.That(total, Is.EqualTo(10));
entities = service.GetPagedChildren(root.Id, UmbracoObjectTypes.Media, 1, 6, out total).ToArray();
Assert.That(entities.Length, Is.EqualTo(4));
Assert.That(total, Is.EqualTo(10));
Assert.That(total, Is.EqualTo(10));
}
[Test]
@@ -362,7 +362,7 @@ namespace Umbraco.Tests.Services
var root = MockedMedia.CreateMediaFolder(folderType, -1);
ServiceContext.MediaService.Save(root);
for (int i = 0; i < 10; i++)
{
var c1 = MockedMedia.CreateMediaImage(imageMediaType, root.Id);
@@ -519,7 +519,7 @@ namespace Umbraco.Tests.Services
Assert.That(
entities.Any(
x =>
x.AdditionalData.Any(y => y.Value is UmbracoEntity.EntityProperty
x.AdditionalData.Any(y => y.Value is UmbracoEntity.EntityProperty
&& ((UmbracoEntity.EntityProperty)y.Value).PropertyEditorAlias == Constants.PropertyEditors.UploadFieldAlias)), Is.True);
}
@@ -575,6 +575,30 @@ namespace Umbraco.Tests.Services
Assert.IsFalse(result2.Success);
}
[Test]
public void ReserveId()
{
var service = ServiceContext.EntityService;
var guid = Guid.NewGuid();
// can reserve
var reservedId = service.ReserveId(guid);
Assert.IsTrue(reservedId > 0);
// can get it back
var id = service.GetIdForKey(guid, UmbracoObjectTypes.DocumentType);
Assert.IsTrue(id.Success);
Assert.AreEqual(reservedId, id.Result);
// anything goes
id = service.GetIdForKey(guid, UmbracoObjectTypes.Media);
Assert.IsTrue(id.Success);
Assert.AreEqual(reservedId, id.Result);
// a random guid won't work
Assert.IsFalse(service.GetIdForKey(Guid.NewGuid(), UmbracoObjectTypes.DocumentType).Success);
}
private static bool _isSetup = false;
private int folderId;
@@ -589,7 +613,7 @@ namespace Umbraco.Tests.Services
//Create and Save folder-Media -> 1050
var folderMediaType = ServiceContext.ContentTypeService.GetMediaType(1031);
var folder = MockedMedia.CreateMediaFolder(folderMediaType, -1);
var folder = MockedMedia.CreateMediaFolder(folderMediaType, -1);
ServiceContext.MediaService.Save(folder, 0);
folderId = folder.Id;
@@ -607,9 +631,9 @@ namespace Umbraco.Tests.Services
ServiceContext.MediaService.Save(subfolder, 0);
var subfolder2 = MockedMedia.CreateMediaFolder(folderMediaType, subfolder.Id);
ServiceContext.MediaService.Save(subfolder2, 0);
}
}
}
}
@@ -118,27 +118,14 @@ namespace Umbraco.Tests.TestHelpers
}
}
/// <summary>
/// By default this returns false which means the plugin manager will not be reset so it doesn't need to re-scan
/// all of the assemblies. Inheritors can override this if plugin manager resetting is required, generally needs
/// to be set to true if the SetupPluginManager has been overridden.
/// </summary>
protected virtual bool PluginManagerResetRequired
{
get { return false; }
}
/// <summary>
/// Inheritors can resset the plugin manager if they choose to on teardown
/// </summary>
protected virtual void ResetPluginManager()
{
if (PluginManagerResetRequired)
{
PluginManager.Current = null;
}
}
{
PluginManager.Current = null;
}
protected virtual CacheHelper CreateCacheHelper()
{
return CacheHelper.CreateDisabledCacheHelper();
@@ -166,10 +153,10 @@ namespace Umbraco.Tests.TestHelpers
var dbFactory = new DefaultDatabaseFactory(Constants.System.UmbracoConnectionName, Logger);
var scopeProvider = new ScopeProvider(dbFactory);
var evtMsgs = new TransientMessagesFactory();
var applicationContext = new ApplicationContext(
//assign the db context
new DatabaseContext(scopeProvider, Logger, sqlSyntax, Constants.DatabaseProviders.SqlCe),
//assign the service context
var applicationContext = new ApplicationContext(
//assign the db context
new DatabaseContext(scopeProvider, Logger, sqlSyntax, Constants.DatabaseProviders.SqlCe),
//assign the service context
new ServiceContext(repoFactory, new PetaPocoUnitOfWorkProvider(scopeProvider), CacheHelper, Logger, evtMsgs),
CacheHelper,
ProfilingLogger)
@@ -185,26 +172,23 @@ namespace Umbraco.Tests.TestHelpers
/// </summary>
protected virtual void SetupPluginManager()
{
if (PluginManager.Current == null || PluginManagerResetRequired)
PluginManager.Current = new PluginManager(
new ActivatorServiceProvider(),
CacheHelper.RuntimeCache, ProfilingLogger, false)
{
PluginManager.Current = new PluginManager(
new ActivatorServiceProvider(),
CacheHelper.RuntimeCache, ProfilingLogger, false)
AssembliesToScan = new[]
{
AssembliesToScan = new[]
{
Assembly.Load("Umbraco.Core"),
Assembly.Load("umbraco"),
Assembly.Load("Umbraco.Tests"),
Assembly.Load("businesslogic"),
Assembly.Load("cms"),
Assembly.Load("controls"),
Assembly.Load("umbraco.editorControls"),
Assembly.Load("umbraco.MacroEngines"),
Assembly.Load("umbraco.providers"),
}
};
}
Assembly.Load("Umbraco.Core"),
Assembly.Load("umbraco"),
Assembly.Load("Umbraco.Tests"),
Assembly.Load("businesslogic"),
Assembly.Load("cms"),
Assembly.Load("controls"),
Assembly.Load("umbraco.editorControls"),
Assembly.Load("umbraco.MacroEngines"),
Assembly.Load("umbraco.providers"),
}
};
}
/// <summary>
+8 -1
View File
@@ -7,12 +7,19 @@ using NUnit.Framework;
using Umbraco.Core;
using Umbraco.Core.Deploy;
using Umbraco.Core.Serialization;
using Umbraco.Tests.TestHelpers;
namespace Umbraco.Tests
{
[TestFixture]
public class UdiTests
public class UdiTests : BaseUmbracoApplicationTest
{
[SetUp]
public void Setup()
{
Udi.ResetUdiTypes();
}
[Test]
public void StringUdiCtorTest()
{
+2 -3
View File
@@ -62,9 +62,8 @@
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<HintPath>..\packages\Castle.Core.4.0.0\lib\net45\Castle.Core.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.83.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.83\lib\net45\Examine.dll</HintPath>
<Private>True</Private>
<Reference Include="Examine, Version=0.1.88.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.88\lib\net45\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
@@ -22,5 +22,38 @@ namespace Umbraco.Tests.Web.Mvc
var expected = "<div><h1>hello world</h1><p>hello world<br />hello world<br />hello world<br />hello world</p></div>";
Assert.AreEqual(expected, output);
}
[Test]
public void TruncateWithElipsis()
{
var output = _htmlStringUtilities.Truncate("hello world", 5, true, false).ToString();
var expected = "hello&hellip;";
Assert.AreEqual(expected, output);
}
[Test]
public void TruncateWithoutElipsis()
{
var output = _htmlStringUtilities.Truncate("hello world", 5, false, false).ToString();
var expected = "hello";
Assert.AreEqual(expected, output);
}
[Test]
public void TruncateShorterWordThanHellip()
{
//http://issues.umbraco.org/issue/U4-10478
var output = _htmlStringUtilities.Truncate("hi", 5, true, false).ToString();
var expected = "hi";
Assert.AreEqual(expected, output);
}
[Test]
public void TruncateAndRemoveSpaceBetweenHellipAndWord()
{
var output = _htmlStringUtilities.Truncate("hello world", 6 /* hello plus space */, true, false).ToString();
var expected = "hello&hellip;";
Assert.AreEqual(expected, output);
}
}
}
@@ -20,8 +20,14 @@ using Umbraco.Web.Templates;
namespace Umbraco.Tests.Web
{
[TestFixture]
public class TemplateUtilitiesTests
public class TemplateUtilitiesTests : BaseUmbracoApplicationTest
{
[SetUp]
public void Setup()
{
Udi.ResetUdiTypes();
}
[TestCase("", "")]
[TestCase("hello href=\"{localLink:1234}\" world ", "hello href=\"/my-test-url\" world ")]
[TestCase("hello href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
+1 -1
View File
@@ -3,7 +3,7 @@
<package id="AspNetWebApi.SelfHost" version="4.0.20710.0" targetFramework="net45" />
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="Castle.Core" version="4.0.0" targetFramework="net45" />
<package id="Examine" version="0.1.83" targetFramework="net45" />
<package id="Examine" version="0.1.88" targetFramework="net45" />
<package id="log4net" version="2.0.8" targetFramework="net45" />
<package id="Log4Net.Async" version="2.0.4" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
@@ -361,11 +361,29 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
rootScope : function(){
return getRootScope();
},
reloadLocation: function() {
var injector = getRootInjector();
var $route = injector.get("$route");
$route.reload();
/**
This will reload the content frame based on it's current route, if pathToMatch is specified it will only reload it if the current
location matches the path
*/
reloadLocation: function(pathToMatch) {
var injector = getRootInjector();
var doChange = true;
if (pathToMatch) {
var $location = injector.get("$location");
var path = $location.path();
if (path != pathToMatch) {
doChange = false;
}
}
if (doChange) {
var $route = injector.get("$route");
$route.reload();
var $rootScope = injector.get("$rootScope");
$rootScope.$apply();
}
},
closeModalWindow: function(rVal) {
@@ -36,7 +36,7 @@ angular.module("umbraco.directives")
// this will greatly improve performance since there's potentially a lot of nodes being rendered = a LOT of watches!
template: '<li ng-class="{\'current\': (node == currentNode), \'has-children\': node.hasChildren}" on-right-click="altSelect(node, $event)">' +
'<div ng-class="getNodeCssClass(node)" ng-swipe-right="options(node, $event)" >' +
'<div ng-class="getNodeCssClass(node)" ng-swipe-right="options(node, $event)" ng-dblclick="load(node)" >' +
//NOTE: This ins element is used to display the search icon if the node is a container/listview and the tree is currently in dialog
//'<ins ng-if="tree.enablelistviewsearch && node.metaData.isContainer" class="umb-tree-node-search icon-search" ng-click="searchNode(node, $event)" alt="searchAltText"></ins>' +
'<ins ng-class="{\'icon-navigation-right\': !node.expanded || node.metaData.isContainer, \'icon-navigation-down\': node.expanded && !node.metaData.isContainer}" ng-click="load(node)">&nbsp;</ins>' +
@@ -110,7 +110,7 @@ Use this directive to generate a thumbnail grid of media items.
itemMinWidth = scope.itemMinWidth;
}
if (scope.itemMinWidth) {
if (scope.itemMinHeight) {
itemMinHeight = scope.itemMinHeight;
}
@@ -41,7 +41,7 @@ function entityResource($q, $http, umbRequestHelper) {
if (!value) {
return "";
}
value = value.replace("#", "");
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
@@ -269,7 +269,8 @@
var isSelected = false;
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id || item.key === selectedItem.key) {
// if item.id is 2147483647 (int.MaxValue) use item.key
if ((item.id !== 2147483647 && item.id === selectedItem.id) || item.key === selectedItem.key) {
isSelected = true;
}
}
@@ -294,7 +295,8 @@
function deselectItem(item, selection) {
for (var i = 0; selection.length > i; i++) {
var selectedItem = selection[i];
if (item.id === selectedItem.id) {
// if item.id is 2147483647 (int.MaxValue) use item.key
if ((item.id !== 2147483647 && item.id === selectedItem.id) || item.key === selectedItem.key) {
selection.splice(i, 1);
item.selected = false;
}
@@ -366,7 +368,7 @@
var item = items[i];
if (checkbox.checked) {
selection.push({ id: item.id });
selection.push({ id: item.id, key: item.key });
} else {
clearSelection = true;
}
@@ -405,7 +407,8 @@
for (var selectedIndex = 0; selection.length > selectedIndex; selectedIndex++) {
var selectedItem = selection[selectedIndex];
if (item.id === selectedItem.id) {
// if item.id is 2147483647 (int.MaxValue) use item.key
if ((item.id !== 2147483647 && item.id === selectedItem.id) || item.key === selectedItem.key) {
numberOfSelectedItem++;
}
}
@@ -17,7 +17,7 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
//add to umbraco installer facts here
var facts = ['Umbraco helped millions of people watch a man jump from the edge of space',
'Over 420 000 websites are currently powered by Umbraco',
'Over 440 000 websites are currently powered by Umbraco',
"At least 2 people have named their cat 'Umbraco'",
'On an average day, more than 1000 people download Umbraco',
'<a target="_blank" href="https://umbraco.tv">umbraco.tv</a> is the premier source of Umbraco video tutorials to get you started',
@@ -31,10 +31,10 @@ angular.module("umbraco.install").factory('installerService', function($rootScop
"At least 4 people have the Umbraco logo tattooed on them",
"'Umbraco' is the danish name for an allen key",
"Umbraco has been around since 2005, that's a looong time in IT",
"More than 550 people from all over the world meet each year in Denmark in June for our annual conference <a target='_blank' href='https://umbra.co/codegarden'>CodeGarden</a>",
"More than 600 people from all over the world meet each year in Denmark in June for our annual conference <a target='_blank' href='https://umbra.co/codegarden'>CodeGarden</a>",
"While you are installing Umbraco someone else on the other side of the planet is probably doing it too",
"You can extend Umbraco without modifying the source code using either JavaScript or C#",
"Umbraco was installed in more than 165 countries in 2015"
"Umbraco has been installed in more than 198 countries"
];
/**
@@ -35,3 +35,10 @@
margin-right: 5px;
color: @gray-7;
}
input.umb-breadcrumbs__add-ancestor {
height: 25px;
margin-top: -2px;
margin-left: 3px;
width: 100px;
}
@@ -126,21 +126,21 @@ ul.color-picker li a {
// Media picker
// --------------------------------------------------
.umb-mediapicker .add-link {
display: inline-block;
height: 120px;
width: 120px;
text-align: center;
color: @gray-8;
border: 2px @gray-8 dashed;
line-height: 120px;
text-decoration: none;
display: flex;
justify-content:center;
align-items:center;
width: 120px;
text-align: center;
color: @gray-8;
border: 2px @gray-8 dashed;
text-decoration: none;
transition: all 150ms ease-in-out;
transition: all 150ms ease-in-out;
&:hover {
color: @turquoise-d1;
border-color: @turquoise;
}
&:hover {
color: @turquoise-d1;
border-color: @turquoise;
}
}
.umb-mediapicker .picked-image {
@@ -165,6 +165,10 @@ ul.color-picker li a {
text-decoration: none;
}
.umb-mediapicker .add-link-square {
height: 120px;
}
.umb-thumbnails{
@@ -207,11 +211,10 @@ ul.color-picker li a {
.umb-mediapicker .umb-sortable-thumbnails li {
flex-direction: column;
margin: 0;
margin: 0 5px 0 0;
padding: 5px;
}
.umb-sortable-thumbnails li:hover a {
display: flex;
justify-content: center;
@@ -219,16 +222,20 @@ ul.color-picker li a {
}
.umb-sortable-thumbnails li img {
max-width:100%;
max-height:100%;
margin:auto;
display:block;
background-image: url(../img/checkered-background.png);
max-width:100%;
max-height:100%;
margin:auto;
display:block;
background-image: url(../img/checkered-background.png);
}
.umb-sortable-thumbnails li img.noScale{
max-width: none !important;
max-height: none !important;
.umb-sortable-thumbnails li img.trashed {
opacity:0.3;
}
.umb-sortable-thumbnails li img.noScale {
max-width: none !important;
max-height: none !important;
}
.umb-sortable-thumbnails .umb-icon-holder {
@@ -254,8 +261,8 @@ ul.color-picker li a {
}
.umb-sortable-thumbnails li:hover .umb-sortable-thumbnails__actions {
opacity: 1;
visibility: visible;
opacity: 1;
visibility: visible;
}
.umb-sortable-thumbnails .umb-sortable-thumbnails__action {
@@ -285,27 +292,27 @@ ul.color-picker li a {
// -------------------------------------------------
.umb-cropper{
position: relative;
position: relative;
}
.umb-cropper img, .umb-cropper-gravity img{
position: relative;
max-width: 100%;
height: auto;
top: 0;
left: 0;
position: relative;
max-width: 100%;
height: auto;
top: 0;
left: 0;
}
.umb-cropper img {
max-width: none;
max-width: none;
}
.umb-cropper .overlay, .umb-cropper-gravity .overlay {
top: 0;
left: 0;
cursor: move;
z-index: 499;
position: absolute;
top: 0;
left: 0;
cursor: move;
z-index: 6001;
position: absolute;
}
.umb-cropper .viewport{
@@ -317,43 +324,43 @@ ul.color-picker li a {
}
.umb-cropper-gravity .viewport{
overflow: hidden;
position: relative;
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
width: 100%;
height: 100%;
}
.umb-cropper .viewport:after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 5999;
-moz-opacity: .75;
opacity: .75;
filter: alpha(opacity=7);
-webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 5999;
-moz-opacity: .75;
opacity: .75;
filter: alpha(opacity=7);
-webkit-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
-moz-box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
box-shadow: inset 0 0 0 20px white,inset 0 0 0 21px rgba(0,0,0,.1),inset 0 0 20px 21px rgba(0,0,0,.2);
}
.umb-cropper-gravity .overlay{
width: 14px;
height: 14px;
text-align: center;
border-radius: 20px;
background: @turquoise;
border: 3px solid @white;
opacity: 0.8;
width: 14px;
height: 14px;
text-align: center;
border-radius: 20px;
background: @turquoise;
border: 3px solid @white;
opacity: 0.8;
}
.umb-cropper-gravity .overlay i {
font-size: 26px;
line-height: 26px;
opacity: 0.8 !important;
font-size: 26px;
line-height: 26px;
opacity: 0.8 !important;
}
.umb-cropper .crop-container {
@@ -361,16 +368,16 @@ ul.color-picker li a {
}
.umb-cropper .crop-slider {
padding: 10px;
border-top: 1px solid @gray-10;
margin-top: 10px;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
@media (min-width: 769px) {
padding: 10px;
border-top: 1px solid @gray-10;
margin-top: 10px;
display: flex;
align-items: center;
justify-content: center;
flex-wrap: wrap;
@media (min-width: 769px) {
padding: 10px 50px 10px 50px;
}
}
}
.umb-cropper .crop-slider i {
@@ -31,7 +31,7 @@
<div class="umb-el-wrap">
<label class="control-label" for="altField"><localize key="templateEditor_fallbackField">Fallback field</localize></label>
<div class="controls">
<select ng-model="vm.altField" >
<select ng-model="vm.altField" id="altField">
<optgroup localize="label" label="@templateEditor_customFields">
<option ng-repeat="(key, value) in vm.properties" value="{{value}}">{{value}}</option>
</optgroup>
@@ -43,6 +43,7 @@
</div>
</div>
</div>
</div>
<!-- Default value -->
<div>
@@ -55,7 +56,7 @@
<div class="umb-el-wrap">
<label class="control-label" for="altText"><localize key="templateEditor_defaultValue">Default value</localize></label>
<div class="controls">
<input type="text" name="altText" ng-model="vm.altText" umb-auto-focus>
<input type="text" id="altText" name="altText" ng-model="vm.altText" umb-auto-focus>
</div>
</div>
</div>
@@ -65,11 +66,13 @@
<div class="control-group umb-control-group">
<div class="umb-el-wrap">
<div class="controls">
<label class="control-label" for="recursive">
<label class="control-label" >
<localize key="templateEditor_recursive">Recursive</localize>
</label>
<input type="checkbox" name="recursive" ng-model="vm.recursive">
<localize key="templateEditor_recursiveDescr">Yes, make it recursive</localize>
<label for="recursive">
<input id="recursive" type="checkbox" name="recursive" ng-model="vm.recursive">
<localize key="templateEditor_recursiveDescr">Yes, make it recursive</localize>
</label>
</div>
</div>
</div>
@@ -134,7 +137,7 @@
<small><localize key="templateEditor_insertedBefore">Will be inserted before the field value</localize></small>
</label>
<div class="controls">
<input type="text" name="insertBefore" class="-full-width-input" ng-model="vm.insertBefore">
<input type="text" id="insertBefore" name="insertBefore" class="-full-width-input" ng-model="vm.insertBefore">
</div>
</div>
</div>
@@ -147,7 +150,7 @@
<small><localize key="templateEditor_insertedAfter">Will be inserted after the field value</localize></small>
</label>
<div class="controls">
<input type="text" name="insertAfter" class="-full-width-input" ng-model="vm.insertAfter">
<input type="text" id="insertAfter" name="insertAfter" class="-full-width-input" ng-model="vm.insertAfter">
</div>
</div>
</div>
@@ -162,8 +165,10 @@
<small><localize key="templateEditor_convertLineBreaksHelp">Replaces line breaks with break html tag</localize></small>
</label>
</div>
<input type="checkbox" name="linebreaks" ng-model="vm.convertLinebreaks">
<localize key="templateEditor_convertLineBreaksDescription">Yes, convert line breaks</localize>
<label for="linebreaks">
<input type="checkbox" id="linebreaks" name="linebreaks" ng-model="vm.convertLinebreaks">
<localize key="templateEditor_convertLineBreaksDescription">Yes, convert line breaks</localize>
</label>
</div>
</div>
</div>
@@ -55,6 +55,46 @@ angular.module("umbraco")
$scope.target = dialogOptions.currentTarget;
}
function onInit() {
if ($scope.startNodeId !== -1) {
entityResource.getById($scope.startNodeId, "media")
.then(function (ent) {
$scope.startNodeId = ent.id;
run();
});
} else {
run();
}
}
function run() {
//default root item
if (!$scope.target) {
if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) {
entityResource.getById($scope.lastOpenedNode, "media")
.then(ensureWithinStartNode, gotoStartNode);
} else {
gotoStartNode();
}
} else {
//if a target is specified, go look it up - generally this target will just contain ids not the actual full
//media object so we need to look it up
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id
var altText = $scope.target.altText;
mediaResource.getById(id)
.then(function (node) {
$scope.target = node;
if (ensureWithinStartNode(node)) {
selectImage(node);
$scope.target.url = mediaHelper.resolveFile(node);
$scope.target.altText = altText;
$scope.openDetailsDialog();
}
},
gotoStartNode);
}
}
$scope.upload = function(v) {
angular.element(".umb-file-dropzone-directive .file-select").click();
};
@@ -106,7 +146,7 @@ angular.module("umbraco")
if (folder.id > 0) {
entityResource.getAncestors(folder.id, "media")
.then(function(anc) {
.then(function(anc) {
$scope.path = _.filter(anc,
function(f) {
return f.path.indexOf($scope.startNodeId) !== -1;
@@ -215,32 +255,6 @@ angular.module("umbraco")
$scope.gotoFolder({ id: $scope.startNodeId, name: "Media", icon: "icon-folder" });
}
//default root item
if (!$scope.target) {
if ($scope.lastOpenedNode && $scope.lastOpenedNode !== -1) {
entityResource.getById($scope.lastOpenedNode, "media")
.then(ensureWithinStartNode, gotoStartNode);
} else {
gotoStartNode();
}
} else {
//if a target is specified, go look it up - generally this target will just contain ids not the actual full
//media object so we need to look it up
var id = $scope.target.udi ? $scope.target.udi : $scope.target.id
var altText = $scope.target.altText;
mediaResource.getById(id)
.then(function(node) {
$scope.target = node;
if (ensureWithinStartNode(node)) {
selectImage(node);
$scope.target.url = mediaHelper.resolveFile(node);
$scope.target.altText = altText;
$scope.openDetailsDialog();
}
},
gotoStartNode);
}
$scope.openDetailsDialog = function() {
$scope.mediaPickerDetailsOverlay = {};
@@ -365,4 +379,7 @@ angular.module("umbraco")
}
}
}
onInit();
});
@@ -55,7 +55,7 @@
<input
type="text"
class="input-foldername input-mini inline"
class="umb-breadcrumbs__add-ancestor"
ng-show="showFolderInput"
ng-model="newFolderName"
ng-keydown="enterSubmitFolder($event)"
@@ -19,7 +19,7 @@
<button
ng-if="vm.dashboard.urlTrackerDisabled === true"
type="button"
class="umb-era-button umb-button--s -blue"
class="umb-era-button umb-button--s -green"
ng-click="vm.enableUrlTracker()">
<span><localize key="redirectUrls_enableUrlTracker">Enable URL Tracker</localize></span>
</button>
@@ -0,0 +1,12 @@
<div>
<input name="numberField" class="umb-editor umb-number"
type="number"
ng-model="model.value"
val-server="value"
min="0"
max="500"
fix-number />
<span class="help-inline" val-msg-for="numberField" val-toggle-msg="number">Not a number</span>
<span class="help-inline" val-msg-for="numberField" val-toggle-msg="valServer">{{propertyForm.requiredField.errorMsg}}</span>
</div>
@@ -1,7 +1,7 @@
//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper) {
function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper, localizationService) {
var unsubscribe;
@@ -154,7 +154,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
}
}
if ($routeParams.section === "settings" && $routeParams.tree === "documentTypes") {
//if the content-picker is being rendered inside the document-type editor, we don't need to process the startnode query
dialogOptions.startNodeId = -1;
@@ -287,8 +286,12 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
entityResource.getUrl(entity.id, entityType).then(function(data){
// update url
angular.forEach($scope.renderModel, function(item){
if(item.id === entity.id) {
item.url = data;
if (item.id === entity.id) {
if (entity.trashed) {
item.url = localizationService.dictionary.general_recycleBin;
} else {
item.url = data;
}
}
});
});
@@ -330,6 +333,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
"icon": item.icon,
"path": item.path,
"url": item.url,
"trashed": item.trashed,
"published": (item.metaData && item.metaData.IsPublished === false && entityType === "Document") ? false : true
// only content supports published/unpublished content so we set everything else to published so the UI looks correct
});
@@ -1,5 +1,8 @@
<div ng-controller="Umbraco.PropertyEditors.ContentPickerController" class="umb-editor umb-contentpicker">
<p ng-if="(renderModel|filter:{trashed:true}).length == 1"><localize key="contentPicker_pickedTrashedItem"></localize></p>
<p ng-if="(renderModel|filter:{trashed:true}).length > 1"><localize key="contentPicker_pickedTrashedItems"></localize></p>
<ng-form name="contentPickerForm">
<div ui-sortable="sortableOptions" ng-model="renderModel">
@@ -1,7 +1,7 @@
//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerController",
function ($rootScope, $scope, dialogService, entityResource, mediaResource, mediaHelper, $timeout, userService, $location) {
function ($rootScope, $scope, dialogService, entityResource, mediaResource, mediaHelper, $timeout, userService, $location, localizationService) {
//check the pre-values for multi-picker
var multiPicker = $scope.model.config.multiPicker && $scope.model.config.multiPicker !== '0' ? true : false;
@@ -25,17 +25,47 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
// the mediaResource has server side auth configured for which the user must have
// access to the media section, if they don't they'll get auth errors. The entityResource
// acts differently in that it allows access if the user has access to any of the apps that
// might require it's use. Therefore we need to use the metatData property to get at the thumbnail
// might require it's use. Therefore we need to use the metaData property to get at the thumbnail
// value.
entityResource.getByIds(ids, "Media").then(function (medias) {
entityResource.getByIds(ids, "Media").then(function(medias) {
_.each(medias, function (media, i) {
// The service only returns item results for ids that exist (deleted items are silently ignored).
// This results in the picked items value to be set to contain only ids of picked items that could actually be found.
// Since a referenced item could potentially be restored later on, instead of changing the selected values here based
// on whether the items exist during a save event - we should keep "placeholder" items for picked items that currently
// could not be fetched. This will preserve references and ensure that the state of an item does not differ depending
// on whether it is simply resaved or not.
// This is done by remapping the int/guid ids into a new array of items, where we create "Deleted item" placeholders
// when there is no match for a selected id. This will ensure that the values being set on save, are the same as before.
medias = _.map(ids,
function(id) {
var found = _.find(medias,
function(m) {
// We could use coercion (two ='s) here .. but not sure if this works equally well in all browsers and
// it's prone to someone "fixing" it at some point without knowing the effects. Rather use toString()
// compares and be completely sure it works.
return m.udi.toString() === id.toString() || m.id.toString() === id.toString();
});
if (found) {
return found;
} else {
return {
name: localizationService.dictionary.mediaPicker_deletedItem,
id: $scope.model.config.idType !== "udi" ? id : null,
udi: $scope.model.config.idType === "udi" ? id : null,
icon: "icon-picture",
thumbnail: null,
trashed: true
};
}
});
//only show non-trashed items
if (media.parentId >= -1) {
if (!media.thumbnail) {
_.each(medias,
function(media, i) {
// if there is no thumbnail, try getting one if the media is not a placeholder item
if (!media.thumbnail && media.id && media.metaData) {
media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
}
@@ -43,12 +73,10 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
if ($scope.model.config.idType === "udi") {
$scope.ids.push(media.udi);
}
else {
} else {
$scope.ids.push(media.id);
}
}
});
});
$scope.sync();
});
@@ -80,8 +108,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
submit: function(model) {
_.each(model.selectedImages, function(media, i) {
if (!media.thumbnail) {
// if there is no thumbnail, try getting one if the media is not a placeholder item
if (!media.thumbnail && media.id && media.metaData) {
media.thumbnail = mediaHelper.resolveFileFromEntity(media, true);
}
@@ -99,10 +127,8 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
$scope.mediaPickerOverlay.show = false;
$scope.mediaPickerOverlay = null;
}
};
};
$scope.sortableOptions = {
@@ -140,5 +166,4 @@ angular.module('umbraco').controller("Umbraco.PropertyEditors.MediaPickerControl
//update the display val again if it has changed from the server
setupViewModel();
};
});
@@ -1,47 +1,47 @@
<div class="umb-editor umb-mediapicker" ng-controller="Umbraco.PropertyEditors.MediaPickerController">
<ul ui-sortable="sortableOptions" ng-model="images" class="umb-sortable-thumbnails">
<li style="width: 120px; height: 100px; overflow: hidden;" ng-repeat="image in images">
<p ng-if="(images|filter:{trashed:true}).length == 1"><localize key="mediaPicker_pickedTrashedItem"></localize></p>
<p ng-if="(images|filter:{trashed:true}).length > 1"><localize key="mediaPicker_pickedTrashedItems"></localize></p>
<!-- IMAGE -->
<img ng-src="{{image.thumbnail}}" alt="" ng-show="image.thumbnail" title="{{image.name}}" />
<div class="flex error">
<ul ui-sortable="sortableOptions" ng-model="images" class="umb-sortable-thumbnails">
<li style="width: 120px; height: 100px; overflow: hidden;" ng-repeat="image in images">
<!-- SVG -->
<img ng-if="image.metaData.umbracoExtension.Value === 'svg'" ng-src="{{image.metaData.umbracoFile.Value}}" alt="" title="{{image.name}}" />
<img ng-if="image.extension === 'svg'" ng-src="{{image.file}}" alt="" />
<!-- IMAGE -->
<img ng-class="{'trashed': image.trashed}" ng-src="{{image.thumbnail}}" alt="" ng-show="image.thumbnail" title="{{image.trashed ? 'Trashed: ' + image.name : image.name}}" />
<!-- FILE -->
<span class="umb-icon-holder" ng-hide="image.thumbnail || image.metaData.umbracoExtension.Value === 'svg' || image.extension === 'svg'">
<i class="icon {{image.icon}} large"></i>
<small>{{image.name}}</small>
</span>
<!-- SVG -->
<img ng-class="{'trashed': image.trashed}" ng-if="image.metaData.umbracoExtension.Value === 'svg'" ng-src="{{image.metaData.umbracoFile.Value}}" alt="" title="{{image.trashed ? 'Trashed: ' + image.name : image.name}}" />
<img ng-class="{'trashed': image.trashed}" ng-if="image.extension === 'svg'" ng-src="{{image.file}}" alt="" />
<div class="umb-sortable-thumbnails__actions">
<a class="umb-sortable-thumbnails__action" href="" ng-click="goToItem(image)">
<i class="icon icon-edit"></i>
</a>
<a class="umb-sortable-thumbnails__action -red" href="" ng-click="remove($index)">
<i class="icon icon-delete"></i>
</a>
</div>
<!-- FILE -->
<span class="umb-icon-holder" ng-hide="image.thumbnail || image.metaData.umbracoExtension.Value === 'svg' || image.extension === 'svg'">
<i class="icon {{image.icon}} large"></i>
<small>{{image.name}}</small>
</span>
</li>
</ul>
<div class="umb-sortable-thumbnails__actions">
<a class="umb-sortable-thumbnails__action" href="" ng-click="goToItem(image)">
<i class="icon icon-edit"></i>
</a>
<a class="umb-sortable-thumbnails__action -red" href="" ng-click="remove($index)">
<i class="icon icon-delete"></i>
</a>
</div>
</li>
<ul class="umb-sortable-thumbnails" ng-if="showAdd()">
<li style="border: none">
<a href="#" class="add-link" ng-click="add()" prevent-default>
<i class="icon icon-add large"></i>
</a>
</li>
</ul>
</ul>
<a href="#" class="add-link" ng-click="add()" ng-class="{'add-link-square': images.length === 0}" ng-if="showAdd()" prevent-default>
<i class="icon icon-add large"></i>
</a>
</div>
<umb-overlay
ng-if="mediaPickerOverlay.show"
model="mediaPickerOverlay"
position="right"
view="mediaPickerOverlay.view">
</umb-overlay>
<umb-overlay
ng-if="mediaPickerOverlay.show"
model="mediaPickerOverlay"
position="right"
view="mediaPickerOverlay.view">
</umb-overlay>
</div>
@@ -1,37 +1,42 @@
function textboxController($scope) {
// macro parameter editor doesn't contains a config object,
// so we create a new one to hold any properties
// so we create a new one to hold any properties
if (!$scope.model.config) {
$scope.model.config = {};
}
if (!$scope.model.config.maxChars) {
$scope.model.config.maxChars = false;
$scope.model.maxlength = false;
if ($scope.model.config && $scope.model.config.maxChars) {
$scope.model.maxlength = true;
}
$scope.model.maxlength = false;
if($scope.model.config.maxChars) {
$scope.model.maxlength = true;
if($scope.model.value == undefined) {
if (!$scope.model.config.maxChars) {
// 500 is the maximum number that can be stored
// in the database, so set it to the max, even
// if no max is specified in the config
$scope.model.config.maxChars = 500;
}
if ($scope.model.maxlength) {
if ($scope.model.value === undefined) {
$scope.model.count = ($scope.model.config.maxChars * 1);
} else {
$scope.model.count = ($scope.model.config.maxChars * 1) - $scope.model.value.length;
}
}
$scope.model.change = function() {
if($scope.model.config.maxChars) {
if($scope.model.value == undefined) {
$scope.model.change = function () {
if ($scope.model.config && $scope.model.config.maxChars) {
if ($scope.model.value === undefined) {
$scope.model.count = ($scope.model.config.maxChars * 1);
} else {
$scope.model.count = ($scope.model.config.maxChars * 1) - $scope.model.value.length;
}
if($scope.model.count < 0) {
if ($scope.model.count < 0) {
$scope.model.value = $scope.model.value.substring(0, ($scope.model.config.maxChars * 1));
$scope.model.count = 0;
}
}
}
}
angular.module('umbraco').controller("Umbraco.PropertyEditors.textboxController", textboxController);
angular.module('umbraco').controller("Umbraco.PropertyEditors.textboxController", textboxController);
@@ -4,11 +4,11 @@
val-server="value"
ng-required="model.validation.mandatory"
ng-trim="false"
ng-keyup="model.change()" />
ng-keyup="model.change()" />
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="valServer"></span>
<span class="help-inline" val-msg-for="textbox" val-toggle-msg="required"><localize key="general_required">Required</localize></span>
<div class="help" ng-if="model.maxlength">
<strong>{{model.count}}</strong>
<localize key="textbox_characters_left">characters left</localize>
</div>
</div>
<div class="help" ng-if="model.maxlength">
<strong>{{model.count}}</strong>
<localize key="textbox_characters_left">characters left</localize>
</div>
</div>
+12 -14
View File
@@ -127,19 +127,18 @@
<Reference Include="dotless.Core, Version=1.5.2.0, Culture=neutral, PublicKeyToken=96b446c9e63eae34, processorArchitecture=MSIL">
<HintPath>..\packages\dotless.1.5.2\lib\dotless.Core.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.83.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.83\lib\net45\Examine.dll</HintPath>
<Private>True</Private>
<Reference Include="Examine, Version=0.1.88.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.88\lib\net45\Examine.dll</HintPath>
</Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
</Reference>
<Reference Include="ImageProcessor, Version=2.5.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.3\lib\net45\ImageProcessor.dll</HintPath>
<Reference Include="ImageProcessor, Version=2.5.6.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.2.5.6\lib\net45\ImageProcessor.dll</HintPath>
</Reference>
<Reference Include="ImageProcessor.Web, Version=4.8.3.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.Web.4.8.3\lib\net45\ImageProcessor.Web.dll</HintPath>
<Reference Include="ImageProcessor.Web, Version=4.8.7.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\ImageProcessor.Web.4.8.7\lib\net45\ImageProcessor.Web.dll</HintPath>
</Reference>
<Reference Include="log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
<HintPath>..\packages\log4net.2.0.8\lib\net45-full\log4net.dll</HintPath>
@@ -163,9 +162,8 @@
<HintPath>..\packages\Microsoft.CodeAnalysis.CSharp.1.0.0\lib\net45\Microsoft.CodeAnalysis.CSharp.dll</HintPath>
</Reference>
<Reference Include="Microsoft.CSharp" />
<Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.2.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.1\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
<Private>True</Private>
<Reference Include="Microsoft.IO.RecyclableMemoryStream, Version=1.2.2.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\Microsoft.IO.RecyclableMemoryStream.1.2.2\lib\net45\Microsoft.IO.RecyclableMemoryStream.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Owin">
<HintPath>..\packages\Microsoft.Owin.3.0.1\lib\net45\Microsoft.Owin.dll</HintPath>
@@ -342,8 +340,8 @@
<Name>umbraco.providers</Name>
</ProjectReference>
<Reference Include="System.Xml.Linq" />
<Reference Include="Umbraco.ModelsBuilder, Version=3.0.7.99, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Umbraco.ModelsBuilder.3.0.7\lib\Umbraco.ModelsBuilder.dll</HintPath>
<Reference Include="Umbraco.ModelsBuilder, Version=3.0.8.100, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Umbraco.ModelsBuilder.3.0.8\lib\Umbraco.ModelsBuilder.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
@@ -2379,9 +2377,9 @@ xcopy "$(ProjectDir)"..\packages\SqlServerCE.4.0.0.1\x86\*.* "$(TargetDir)x86\"
<WebProjectProperties>
<UseIIS>True</UseIIS>
<AutoAssignPort>True</AutoAssignPort>
<DevelopmentServerPort>7670</DevelopmentServerPort>
<DevelopmentServerPort>7612</DevelopmentServerPort>
<DevelopmentServerVPath>/</DevelopmentServerVPath>
<IISUrl>http://localhost:7670</IISUrl>
<IISUrl>http://localhost:7612</IISUrl>
<NTLMAuthentication>False</NTLMAuthentication>
<UseCustomServer>False</UseCustomServer>
<CustomServerUrl>
@@ -781,7 +781,7 @@
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">Třídění bylo ukončeno.</key>
<key alias="sortHelp">Abyste nastavili, jak mají být položky seřazeny, přetáhněte jednotlivé z nich nahoru či dolů. Anebo klikněte na hlavičku sloupce pro setřídění celé kolekce</key>
<key alias="sortPleaseWait"><![CDATA[ Čekejte, prosím. Položky jsou tříděny a může to chvíli trvat.<br/> <br/> Během třídění nezavírejte toto okno]]></key>
<key alias="sortPleaseWait"><![CDATA[ Čekejte, prosím. Položky jsou tříděny a může to chvíli trvat.]]></key>
</area>
<area alias="speechBubbles">
<key alias="contentPublishedFailedByEvent">Publikování bylo zrušeno doplňkem třetí strany</key>
@@ -736,7 +736,7 @@ Vennlig hilsen Umbraco roboten
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">Sortering ferdig.</key>
<key alias="sortHelp">Dra elementene opp eller ned for å arrangere dem. Du kan også klikke kolonneoverskriftene for å sortere alt på en gang.</key>
<key alias="sortPleaseWait"><![CDATA[Vennligst vent. Elementene blir sortert, dette kan ta litt tid.<br/> <br/> Ikke lukk dette vinduet under sortering]]></key>
<key alias="sortPleaseWait"><![CDATA[Vennligst vent. Elementene blir sortert, dette kan ta litt tid.]]></key>
</area>
<area alias="speechBubbles">
<key alias="operationFailedHeader">En feil oppsto</key>
@@ -912,7 +912,7 @@
<key alias="sortCreationDate">增添時間</key>
<key alias="sortDone">排序完成。</key>
<key alias="sortHelp">上下拖拽項目或按一下列頭進行排序</key>
<key alias="sortPleaseWait"><![CDATA[ 請稍後。項目正在排序,這需要一點時間。<br/> <br/>排序中請不要關閉視窗。]]></key>
<key alias="sortPleaseWait"><![CDATA[ 請稍後。項目正在排序,這需要一點時間。]]></key>
</area>
<area alias="speechBubbles">
<key alias="validationFailedHeader">驗證</key>
@@ -60,21 +60,29 @@
JObject cfg = contentItem.config;
if(cfg != null)
foreach (JProperty property in cfg.Properties()) {
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
foreach (JProperty property in cfg.Properties())
{
var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
}
JObject style = contentItem.styles;
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
{
var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
}
}
if (cssVals.Any())
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
if (cssVals.Any())
attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\"");
}
return new MvcHtmlString(string.Join(" ", attrs));
}
}
@@ -60,21 +60,29 @@
JObject cfg = contentItem.config;
if(cfg != null)
foreach (JProperty property in cfg.Properties()) {
attrs.Add(property.Name + "='" + property.Value.ToString() + "'");
foreach (JProperty property in cfg.Properties())
{
var propertyValue = HttpUtility.HtmlAttributeEncode(property.Value.ToString());
attrs.Add(property.Name + "=\"" + propertyValue + "\"");
}
JObject style = contentItem.styles;
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
cssVals.Add(property.Name + ":" + property.Value.ToString() + ";");
if (style != null) {
var cssVals = new List<string>();
foreach (JProperty property in style.Properties())
{
var propertyValue = property.Value.ToString();
if (string.IsNullOrWhiteSpace(propertyValue) == false)
{
cssVals.Add(property.Name + ":" + propertyValue + ";");
}
}
if (cssVals.Any())
attrs.Add("style='" + string.Join(" ", cssVals) + "'");
if (cssVals.Any())
attrs.Add("style=\"" + HttpUtility.HtmlAttributeEncode(string.Join(" ", cssVals)) + "\"");
}
return new MvcHtmlString(string.Join(" ", attrs));
}
}
@@ -4,8 +4,8 @@
@if (Model.editor.config.markup != null)
{
string markup = Model.editor.config.markup.ToString();
markup = markup.Replace("#value#", Model.value.ToString());
var umbracoHelper = new UmbracoHelper(UmbracoContext.Current);
markup = markup.Replace("#value#", umbracoHelper.ReplaceLineBreaksForHtml(HttpUtility.HtmlEncode(Model.value.ToString())));
markup = markup.Replace("#style#", Model.editor.config.style.ToString());
<text>
@@ -14,6 +14,7 @@
<plugin name="Filter" type="ImageProcessor.Web.Processors.Filter, ImageProcessor.Web" />
<plugin name="Flip" type="ImageProcessor.Web.Processors.Flip, ImageProcessor.Web" />
<plugin name="Format" type="ImageProcessor.Web.Processors.Format, ImageProcessor.Web" enabled="true" />
<plugin name="Gamma" type="ImageProcessor.Web.Processors.Gamma, ImageProcessor.Web" />
<plugin name="GaussianBlur" type="ImageProcessor.Web.Processors.GaussianBlur, ImageProcessor.Web">
<settings>
<setting key="MaxSize" value="22" />
+6 -6
View File
@@ -4,10 +4,10 @@
<package id="ClientDependency" version="1.9.2" targetFramework="net45" />
<package id="ClientDependency-Mvc5" version="1.8.0.0" targetFramework="net45" />
<package id="dotless" version="1.5.2" targetFramework="net45" />
<package id="Examine" version="0.1.83" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.3" targetFramework="net45" />
<package id="ImageProcessor.Web" version="4.8.3" targetFramework="net45" />
<package id="ImageProcessor.Web.Config" version="2.3.0" targetFramework="net45" />
<package id="Examine" version="0.1.88" targetFramework="net45" />
<package id="ImageProcessor" version="2.5.6" targetFramework="net45" />
<package id="ImageProcessor.Web" version="4.8.7" targetFramework="net45" />
<package id="ImageProcessor.Web.Config" version="2.3.1" targetFramework="net45" />
<package id="log4net" version="2.0.8" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
@@ -22,7 +22,7 @@
<package id="Microsoft.CodeAnalysis.Analyzers" version="1.0.0" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.Common" version="1.0.0" targetFramework="net45" />
<package id="Microsoft.CodeAnalysis.CSharp" version="1.0.0" targetFramework="net45" />
<package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.1" targetFramework="net45" />
<package id="Microsoft.IO.RecyclableMemoryStream" version="1.2.2" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net45" />
@@ -37,5 +37,5 @@
<package id="SqlServerCE" version="4.0.0.1" targetFramework="net45" />
<package id="System.Collections.Immutable" version="1.1.36" targetFramework="net45" />
<package id="System.Reflection.Metadata" version="1.0.21" targetFramework="net45" />
<package id="Umbraco.ModelsBuilder" version="3.0.7" targetFramework="net45" />
<package id="Umbraco.ModelsBuilder" version="3.0.8" targetFramework="net45" />
</packages>
+10 -1
View File
@@ -784,6 +784,15 @@ Mange hilsner fra Umbraco robotten
<area alias="colorpicker">
<key alias="noColors">Du har ikke konfigureret nogen godkendte farver</key>
</area>
<area alias="contentPicker">
<key alias="pickedTrashedItem">Du har valgt et dokument som er slettet eller lagt i papirkurven</key>
<key alias="pickedTrashedItems">Du har valgt dokumenter som er slettede eller lagt i papirkurven</key>
</area>
<area alias="mediaPicker">
<key alias="pickedTrashedItem">Du har valgt et medie som er slettet eller lagt i papirkurven</key>
<key alias="pickedTrashedItems">Du har valgt medier som er slettede eller lagt i papirkurven</key>
<key alias="deletedItem">Slettet medie</key>
</area>
<area alias="relatedlinks">
<key alias="enterExternal">indtast eksternt link</key>
<key alias="chooseInternal">vælg en intern side</key>
@@ -955,7 +964,7 @@ Mange hilsner fra Umbraco robotten
<key alias="sortCreationDate">Oprettelsesdato</key>
<key alias="sortDone">Sortering udført</key>
<key alias="sortHelp">Træk de forskellige sider op eller ned for at indstille hvordan de skal arrangeres, eller klik på kolonnehovederne for at sortere hele rækken af sider</key>
<key alias="sortPleaseWait"><![CDATA[Vent venligst mens siderne sorteres. Det kan tage et stykke tid<br/><br/>Luk ikke dette vindue imens]]></key>
<key alias="sortPleaseWait"><![CDATA[Vent venligst mens siderne sorteres. Det kan tage et stykke tid.]]></key>
</area>
<area alias="speechBubbles">
<key alias="operationCancelledHeader">Annulleret</key>
@@ -773,7 +773,7 @@ Wenn Sie sich für Runway entscheiden, können Sie optional Blöcke nutzen, die
<key alias="sortCreationDate">Erstellungsdatum</key>
<key alias="sortDone">Sortierung abgeschlossen.</key>
<key alias="sortHelp">Ziehen Sie die Elemente an ihre gewünschte neue Position.</key>
<key alias="sortPleaseWait">Bitte warten, die Seiten werden sortiert. Das kann einen Moment dauern. Bitte schließen Sie dieses Fenster nicht, bis der Sortiervorgang abgeschlossen ist.</key>
<key alias="sortPleaseWait">Bitte warten, die Seiten werden sortiert. Das kann einen Moment dauern.</key>
</area>
<area alias="speechBubbles">
<key alias="operationFailedHeader">Fehlgeschlagen</key>
+10 -1
View File
@@ -952,6 +952,15 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="colorpicker">
<key alias="noColors">You have not configured any approved colours</key>
</area>
<area alias="contentPicker">
<key alias="pickedTrashedItem">You have picked a content item currently deleted or in the recycle bin</key>
<key alias="pickedTrashedItems">You have picked content items currently deleted or in the recycle bin</key>
</area>
<area alias="mediaPicker">
<key alias="pickedTrashedItem">You have picked a media item currently deleted or in the recycle bin</key>
<key alias="pickedTrashedItems">You have picked media items currently deleted or in the recycle bin</key>
<key alias="deletedItem">Deleted item</key>
</area>
<area alias="relatedlinks">
<key alias="enterExternal">enter external link</key>
<key alias="chooseInternal">choose internal page</key>
@@ -1029,7 +1038,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">Sorting complete.</key>
<key alias="sortHelp">Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items</key>
<key alias="sortPleaseWait"><![CDATA[ Please wait. Items are being sorted, this can take a while.<br/> <br/> Do not close this window during sorting]]></key>
<key alias="sortPleaseWait"><![CDATA[ Please wait. Items are being sorted, this can take a while.]]></key>
</area>
<area alias="speechBubbles">
<key alias="validationFailedHeader">Validation</key>
@@ -1011,6 +1011,15 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="colorpicker">
<key alias="noColors">You have not configured any approved colors</key>
</area>
<area alias="contentPicker">
<key alias="pickedTrashedItem">You have picked a content item currently deleted or in the recycle bin</key>
<key alias="pickedTrashedItems">You have picked content items currently deleted or in the recycle bin</key>
</area>
<area alias="mediaPicker">
<key alias="pickedTrashedItem">You have picked a media item currently deleted or in the recycle bin</key>
<key alias="pickedTrashedItems">You have picked media items currently deleted or in the recycle bin</key>
<key alias="deletedItem">Deleted item</key>
</area>
<area alias="relatedlinks">
<key alias="enterExternal">enter external link</key>
<key alias="chooseInternal">choose internal page</key>
@@ -1088,7 +1097,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">Sorting complete.</key>
<key alias="sortHelp">Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items</key>
<key alias="sortPleaseWait"><![CDATA[ Please wait. Items are being sorted, this can take a while.<br/> <br/> Do not close this window during sorting]]></key>
<key alias="sortPleaseWait"><![CDATA[ Please wait. Items are being sorted, this can take a while.]]></key>
</area>
<area alias="speechBubbles">
<key alias="validationFailedHeader">Validation</key>
@@ -688,8 +688,7 @@
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">Ordenación completa.</key>
<key alias="sortHelp">Arrastra las diferentes páginas debajo para colocarlas como deberían estar. O haz click en las cabeceras de las columnas para ordenar todas las páginas</key>
<key alias="sortPleaseWait"><![CDATA[Espere por favor, las páginas están siendo ordenadas. El proceso puede durar un poco.<br/>
<br/> No cierre esta ventana mientras se está ordenando ]]></key>
<key alias="sortPleaseWait"><![CDATA[Espere por favor, las páginas están siendo ordenadas. El proceso puede durar un poco.]]></key>
</area>
<area alias="speechBubbles">
<key alias="contentPublishedFailedByEvent">La publicación fue cancelada por un complemento de terceros</key>
@@ -1026,7 +1026,7 @@ Pour gérer votre site, ouvrez simplement le backoffice Umbraco et commencez à
<key alias="sortCreationDate">Date de création</key>
<key alias="sortDone">Tri achevé.</key>
<key alias="sortHelp">Faites glisser les différents éléments vers le haut ou vers le bas pour définir la manière dont ils doivent être organisés. Ou cliquez sur les entêtes de colonnes pour trier la collection complète d'éléments</key>
<key alias="sortPleaseWait"><![CDATA[Veuillez patienter. Les éléments sont en cours de tri, cela peut prendre un moment.<br/> <br/> Ne fermez pas cette fenêtre durant le tri.]]></key>
<key alias="sortPleaseWait"><![CDATA[Veuillez patienter. Les éléments sont en cours de tri, cela peut prendre un moment.]]></key>
</area>
<area alias="speechBubbles">
<key alias="validationFailedHeader">Validation</key>
@@ -693,7 +693,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">המיון הושלם.</key>
<key alias="sortHelp">יש לגרור את הפריטים מעלה או מטה כדי להגדיר את סדר התוכן. או לחץ על כותרת העמודה כדי למיין את כל פריטי התוכן</key>
<key alias="sortPleaseWait"><![CDATA[פריטי התוכן ממיונים ברגע זה, תהליך זה לוקח זמן מה.<br/> <br/> נא לא לסגור את החלון בזמן המיון]]></key>
<key alias="sortPleaseWait"><![CDATA[פריטי התוכן ממיונים ברגע זה, תהליך זה לוקח זמן מה.]]></key>
</area>
<area alias="speechBubbles">
<key alias="contentPublishedFailedByEvent">הפירסום בוטל על ידי תוסף צד שלישי</key>
@@ -659,7 +659,7 @@ Per gestire il tuo sito web, è sufficiente aprire il back office di Umbraco e i
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone"><![CDATA[Ordinamento completato.]]></key>
<key alias="sortHelp"><![CDATA[Sposta su o giù le pagine trascinandole per determinarne l'ordinamento. Oppure clicca la testata della colonna per ordinare l'intero gruppo di pagine]]></key>
<key alias="sortPleaseWait"><![CDATA[Si prega di attendere. Gli elementi sono in fase di ordinamento, questo può richiedere del tempo.<br/> <br/>Non chiudere questa finestra durante l'operazione]]></key>
<key alias="sortPleaseWait"><![CDATA[Si prega di attendere. Gli elementi sono in fase di ordinamento, questo può richiedere del tempo.]]></key>
</area>
<area alias="speechBubbles">
<key alias="contentPublishedFailedByEvent"><![CDATA[La pubblicazione è stata cancellata da un add-in di terze parti.]]></key>
@@ -898,7 +898,7 @@ Runwayをインストールして作られた新しいウェブサイトがど
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">ソートが完了しました。</key>
<key alias="sortHelp">上下にアイテムをドラッグするなどして適当に配置したり、列のヘッダーをクリックしてコレクション全体をソートできます</key>
<key alias="sortPleaseWait"><![CDATA[ 項目の並べ替えには少し時間がかかります。しばらくお待ちください。<br/> <br/> 並び替え中はウィンドウを閉じないでください。]]></key>
<key alias="sortPleaseWait"><![CDATA[ 項目の並べ替えには少し時間がかかります。しばらくお待ちください。]]></key>
</area>
<area alias="speechBubbles">
<key alias="validationFailedHeader">検証</key>
@@ -669,7 +669,7 @@
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">정렬 완료</key>
<key alias="sortHelp">다른 아이템을 마우스로 위,아래로 드래그 하여 이동하거나 열의 헤더를 클릭하여 아이템을 정렬할 수 있습니다</key>
<key alias="sortPleaseWait"><![CDATA[잠시 기다리십시오. 아이템을 정렬 하는데 잠시 시간이 소요될 수 있습니다<br/><br/>정렬하는 동안 이 창을 닫지 마십시오]]></key>
<key alias="sortPleaseWait"><![CDATA[잠시 기다리십시오. 아이템을 정렬 하는데 잠시 시간이 소요될 수 있습니다]]></key>
</area>
<area alias="speechBubbles">
<key alias="contentPublishedFailedByEvent">3rd party add-in 때문에 발행이 취소되었습니다.</key>
@@ -954,7 +954,7 @@ Echter, Runway biedt een gemakkelijke basis om je snel op weg te helpen. Als je
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">Sorteren gereed.</key>
<key alias="sortHelp">Sleep de pagina's omhoog of omlaag om de volgorde te veranderen. Of klik op de kolom-header om alle pagina's daarop te sorteren.</key>
<key alias="sortPleaseWait"><![CDATA[Een ogenblik geduld. Paginas worden gesorteerd, dit kan even duren.<br/> <br/> Sluit dit venster niet tijdens het sorteren]]></key>
<key alias="sortPleaseWait"><![CDATA[Een ogenblik geduld. Paginas worden gesorteerd, dit kan even duren.]]></key>
</area>
<area alias="speechBubbles">
<key alias="validationFailedHeader">Validatie</key>
@@ -1025,7 +1025,7 @@ Naciśnij przycisk <strong>instaluj</strong>, aby zainstalować bazę danych Umb
<key alias="sortCreationDate">Data utworzenia</key>
<key alias="sortDone">Sortowanie zakończone.</key>
<key alias="sortHelp">Przesuń poszczególne elementy w górę oraz w dół aż będą w odpowiedniej kolejności lub kliknij na nagłówku kolumny, aby posortować całą kolekcję elementów</key>
<key alias="sortPleaseWait"><![CDATA[Proszę czekać. Trwa sortowanie elementów.<br/><br/>Nie zamykaj tego okna podczas sortowania]]></key>
<key alias="sortPleaseWait"><![CDATA[Proszę czekać. Trwa sortowanie elementów.]]></key>
</area>
<area alias="speechBubbles">
<key alias="validationFailedHeader">Walidacja</key>
@@ -658,7 +658,7 @@ Você pode publicar esta página e todas suas sub-páginas ao selecionar <em>pub
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">Classificação concluída.</key>
<key alias="sortHelp">Arraste os diferentes itens para cima ou para baixo para definir como os mesmos serão arranjados. Ou clique no título da coluna para classificar a coleção completa de itens</key>
<key alias="sortPleaseWait"><![CDATA[Favor esperar. Itens estão sendo classificados, isto pode demorar um tempo. <br /><br /> Não feche esta janela durante a classificação]]></key>
<key alias="sortPleaseWait"><![CDATA[Favor esperar. Itens estão sendo classificados, isto pode demorar um tempo.]]></key>
</area>
<area alias="speechBubbles">
<key alias="contentPublishedFailedByEvent">Publicação foi cancelada por add-in de terceiros</key>
@@ -1243,7 +1243,7 @@
<key alias="sortCreationDate">Дата создания</key>
<key alias="sortDone">Сортировка завершена</key>
<key alias="sortHelp">Перетаскивайте элементы на нужное место вверх или вниз для определения необходимого Вам порядка сортировки. Также можно использовать заголовки столбцов, чтобы отсортировать все элементы сразу.</key>
<key alias="sortPleaseWait"><![CDATA[Пожалуйста, подождите... Страницы сортируются, это может занять некоторое время.<br/> <br/> Не закрывайте это окно до окончания процесса сортировки.]]></key>
<key alias="sortPleaseWait"><![CDATA[Пожалуйста, подождите... Страницы сортируются, это может занять некоторое время.]]></key>
</area>
<area alias="speechBubbles">
<key alias="contentPublishedFailedByEvent">Процесс публикации был отменен установленным пакетом дополнений.</key>
@@ -697,7 +697,7 @@
<key alias="sortCreationDate">Creation date</key>
<key alias="sortDone">Sortering klar</key>
<key alias="sortHelp">Välj i vilken ordning du vill ha sidorna genom att dra dem upp eller ner i listan. Du kan också klicka på kolumnrubrikerna för att sortera grupper av sidor</key>
<key alias="sortPleaseWait"><![CDATA[Vänta medan sidorna sorteras. Det kan ta en stund.<br/><br/>Stäng inte fönstret under tiden sidorna sorteras.]]></key>
<key alias="sortPleaseWait"><![CDATA[Vänta medan sidorna sorteras. Det kan ta en stund.]]></key>
</area>
<area alias="speechBubbles">
<key alias="contentPublishedFailedByEvent">Publiceringen avbröts av ett tredjepartstillägg</key>
@@ -855,7 +855,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="sort">
<key alias="sortDone">Sorting complete.</key>
<key alias="sortHelp">Drag the different items up or down below to set how they should be arranged. Or click the column headers to sort the entire collection of items</key>
<key alias="sortPleaseWait"><![CDATA[ Please wait. Items are being sorted, this can take a while.<br/> <br/> Do not close this window during sorting]]></key>
<key alias="sortPleaseWait"><![CDATA[ Please wait. Items are being sorted, this can take a while.]]></key>
</area>
<area alias="speechBubbles">
<key alias="operationFailedHeader">Hata</key>
@@ -1080,7 +1080,7 @@
<key alias="sortCreationDate">创建日期</key>
<key alias="sortDone">排序完成。</key>
<key alias="sortHelp">上下拖拽项目或单击列头进行排序</key>
<key alias="sortPleaseWait"><![CDATA[正在排序请稍候…<br/><br/>请不要关闭窗口]]></key>
<key alias="sortPleaseWait"><![CDATA[正在排序请稍候…]]></key>
</area>
<area alias="speechBubbles">
<key alias="validationFailedHeader">验证</key>
@@ -30,7 +30,7 @@ namespace Umbraco.Web.UI.Umbraco.Dialogs
}
DocumentId = doc.Id;
PageName = doc.Name;
PageName = Server.HtmlEncode(doc.Name);
DocumentPath = doc.Path;
}
@@ -172,9 +172,9 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
},
reloadLocation: function () {
reloadLocation: function (pathToMatch) {
if (this.mainWindow().UmbClientMgr) {
this.mainWindow().UmbClientMgr.reloadLocation();
this.mainWindow().UmbClientMgr.reloadLocation(pathToMatch);
}
},
@@ -105,6 +105,7 @@
//wire up the submit button
self._opts.submitButton.click(function() {
this.disabled = true;
self._saveSort();
});
@@ -139,9 +139,7 @@ namespace Umbraco.Web.Editors
//get the user
var user = Security.GetBackOfficeUser(loginModel.Username);
if (UserManager != null)
UserManager.RaiseLoginSuccessEvent(user.Id);
UserManager.RaiseLoginSuccessEvent(user.Id);
return SetPrincipalAndReturnUserDetail(user);
case SignInStatus.RequiresVerification:
@@ -177,8 +175,7 @@ namespace Umbraco.Web.Editors
userId = attemptedUser.Id
});
if (UserManager != null)
UserManager.RaiseLoginRequiresVerificationEvent(attemptedUser.Id);
UserManager.RaiseLoginRequiresVerificationEvent(attemptedUser.Id);
return verifyResponse;
@@ -225,10 +222,9 @@ namespace Umbraco.Web.Editors
Services.TextService.Localize("login/resetPasswordEmailCopySubject",
//Ensure the culture of the found user is used for the email!
UserExtensions.GetUserCulture(identityUser.Culture, Services.TextService)),
message);
if(UserManager != null)
UserManager.RaiseForgotPasswordRequestedEvent(user.Id);
message);
UserManager.RaiseForgotPasswordRequestedEvent(user.Id);
}
}
@@ -295,14 +291,10 @@ namespace Umbraco.Web.Editors
switch (result)
{
case SignInStatus.Success:
if (UserManager != null)
UserManager.RaiseLoginSuccessEvent(user.Id);
UserManager.RaiseLoginSuccessEvent(user.Id);
return SetPrincipalAndReturnUserDetail(user);
case SignInStatus.LockedOut:
if (UserManager != null)
UserManager.RaiseAccountLockedEvent(user.Id);
UserManager.RaiseAccountLockedEvent(user.Id);
return Request.CreateValidationErrorResponse("User is locked out");
case SignInStatus.Failure:
default:
@@ -325,16 +317,16 @@ namespace Umbraco.Web.Editors
{
Logger.Info<AuthenticationController>(
"User {0} is currently locked out, unlocking and resetting AccessFailedCount",
() => model.UserId);
() => model.UserId);
//var user = await UserManager.FindByIdAsync(model.UserId);
var unlockResult = await UserManager.SetLockoutEndDateAsync(model.UserId, DateTimeOffset.Now);
if(unlockResult.Succeeded == false)
var unlockResult = await UserManager.SetLockoutEndDateAsync(model.UserId, DateTimeOffset.Now);
if(unlockResult.Succeeded == false)
{
Logger.Warn<AuthenticationController>("Could not unlock for user {0} - error {1}",
() => model.UserId, () => unlockResult.Errors.First());
}
() => model.UserId, () => unlockResult.Errors.First());
}
var resetAccessFailedCountResult = await UserManager.ResetAccessFailedCountAsync(model.UserId);
if (resetAccessFailedCountResult.Succeeded == false)
{
@@ -342,9 +334,8 @@ namespace Umbraco.Web.Editors
() => model.UserId, () => unlockResult.Errors.First());
}
}
if(UserManager != null)
UserManager.RaiseForgotPasswordChangedSuccessEvent(model.UserId);
UserManager.RaiseForgotPasswordChangedSuccessEvent(model.UserId);
return Request.CreateResponse(HttpStatusCode.OK);
}
return Request.CreateValidationErrorResponse(
+1 -4
View File
@@ -520,11 +520,8 @@ namespace Umbraco.Web.Editors
{
var contentService = Services.ContentService;
// content service GetByIds does order the content items based on the order of Ids passed in
var content = contentService.GetByIds(sorted.IdSortOrder);
// Save content with new sort order and update content xml in db accordingly
if (contentService.Sort(content) == false)
if (contentService.Sort(sorted.IdSortOrder) == false)
{
LogHelper.Warn<ContentController>("Content sorting failed, this was probably caused by an event being cancelled");
return Request.CreateValidationErrorResponse("Content sorting failed, this was probably caused by an event being cancelled");
+71 -22
View File
@@ -413,7 +413,69 @@ namespace Umbraco.Web.Editors
}
return Request.CreateResponse(HttpStatusCode.OK);
}
}
/// <summary>
/// Given a parent id which could be a GUID, UDI or an INT, this will resolve the INT
/// </summary>
/// <param name="parentId"></param>
/// <param name="validatePermissions">
/// If true, this will check if the current user has access to the resolved integer parent id
/// and if that check fails an unauthorized exception will occur
/// </param>
/// <returns></returns>
private int GetParentIdAsInt(string parentId, bool validatePermissions)
{
int intParentId;
GuidUdi parentUdi;
// test for udi
if (GuidUdi.TryParse(parentId, out parentUdi))
{
parentId = parentUdi.Guid.ToString();
}
//if it's not an INT then we'll check for GUID
if (int.TryParse(parentId, out intParentId) == false)
{
// if a guid then try to look up the entity
Guid idGuid;
if (Guid.TryParse(parentId, out idGuid))
{
var entity = Services.EntityService.GetByKey(idGuid);
if (entity != null)
{
intParentId = entity.Id;
}
else
{
throw new EntityNotFoundException(parentId, "The passed id doesn't exist");
}
}
else
{
throw new HttpResponseException(
Request.CreateValidationErrorResponse("The request was not formatted correctly, the parentId is not an integer, Guid or UDI"));
}
}
//ensure the user has access to this folder by parent id!
if (CheckPermissions(
new Dictionary<string, object>(),
Security.CurrentUser,
Services.MediaService,
intParentId) == false)
{
throw new HttpResponseException(Request.CreateResponse(
HttpStatusCode.Forbidden,
new SimpleNotificationModel(new Notification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"),
SpeechBubbleIcon.Warning))));
}
return intParentId;
}
/// <summary>
/// Change the sort order for media
@@ -574,11 +636,13 @@ namespace Umbraco.Web.Editors
}
}
[EnsureUserPermissionForMedia("folder.ParentId")]
public MediaItemDisplay PostAddFolder(EntityBasic folder)
public MediaItemDisplay PostAddFolder(PostedFolder folder)
{
var mediaService = ApplicationContext.Services.MediaService;
var f = mediaService.CreateMedia(folder.Name, folder.ParentId, Constants.Conventions.MediaTypes.Folder);
var intParentId = GetParentIdAsInt(folder.ParentId, validatePermissions: true);
var mediaService = ApplicationContext.Services.MediaService;
var f = mediaService.CreateMedia(folder.Name, intParentId, Constants.Conventions.MediaTypes.Folder);
mediaService.Save(f, Security.CurrentUser.Id);
return Mapper.Map<IMedia, MediaItemDisplay>(f);
@@ -649,21 +713,6 @@ namespace Umbraco.Web.Editors
}
}
//ensure the user has access to this folder by parent id!
if (CheckPermissions(
new Dictionary<string, object>(),
Security.CurrentUser,
Services.MediaService, parentId) == false)
{
return Request.CreateResponse(
HttpStatusCode.Forbidden,
new SimpleNotificationModel(new Notification(
Services.TextService.Localize("speechBubbles/operationFailedHeader"),
Services.TextService.Localize("speechBubbles/invalidUserPermissionsText"),
SpeechBubbleIcon.Warning)));
}
var tempFiles = new PostedFiles();
var mediaService = ApplicationContext.Services.MediaService;
@@ -793,8 +842,8 @@ namespace Umbraco.Web.Editors
if (origin.Value == "blueimp")
{
return Request.CreateResponse(HttpStatusCode.OK,
tempFiles,
//Don't output the angular xsrf stuff, blue imp doesn't like that
tempFiles,
//Don't output the angular xsrf stuff, blue imp doesn't like that
new JsonMediaTypeFormatter());
}
}
@@ -140,7 +140,7 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
public override IEnumerable<HealthCheckStatus> GetStatus()
{
var successMessage = string.Format(CheckSuccessMessage, FileName, XPath, Values, CurrentValue);
var successMessage = string.Format(CheckSuccessMessage, FileName, XPath, Values);
var configValue = _configurationService.GetConfigurationValue();
if (configValue.Success == false)
@@ -156,6 +156,9 @@ namespace Umbraco.Web.HealthCheck.Checks.Config
CurrentValue = configValue.Result;
// need to update the successMessage with the CurrentValue
successMessage = string.Format(CheckSuccessMessage, FileName, XPath, Values, CurrentValue);
var valueFound = Values.Any(value => string.Equals(CurrentValue, value.Value, StringComparison.InvariantCultureIgnoreCase));
if (ValueComparisonType == ValueComparisonType.ShouldEqual && valueFound || ValueComparisonType == ValueComparisonType.ShouldNotEqual && valueFound == false)
{
@@ -3,12 +3,14 @@ using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using Umbraco.Web.Editors;
using Umbraco.Web.WebApi.Filters;
namespace Umbraco.Web.HealthCheck
{
/// <summary>
/// The API controller used to display the health check info and execute any actions
/// </summary>
[UmbracoApplicationAuthorize(Core.Constants.Applications.Developer)]
public class HealthCheckController : UmbracoAuthorizedJsonController
{
private readonly IHealthCheckResolver _healthCheckResolver;
+6 -2
View File
@@ -92,6 +92,7 @@ namespace Umbraco.Web
using (var outputms = new MemoryStream())
{
bool lengthReached = false;
using (var outputtw = new StreamWriter(outputms))
{
using (var ms = new MemoryStream())
@@ -106,7 +107,6 @@ namespace Umbraco.Web
using (TextReader tr = new StreamReader(ms))
{
bool isInsideElement = false,
lengthReached = false,
insideTagSpaceEncountered = false,
isTagClose = false;
@@ -254,10 +254,14 @@ namespace Umbraco.Web
//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)
if (addElipsis && lengthReached && string.IsNullOrWhiteSpace(firstTrim) == false)
{
result = firstTrim[firstTrim.Length - hellip.Length - 1] == ' ' ? firstTrim.Remove(firstTrim.Length - hellip.Length - 1, 1) : firstTrim;
}
else
{
result = firstTrim;
}
return new HtmlString(result);
}
}
@@ -2,10 +2,7 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Umbraco.Core;
using Umbraco.Core.Models.Validation;
@@ -0,0 +1,17 @@
using System.Runtime.Serialization;
namespace Umbraco.Web.Models.ContentEditing
{
/// <summary>
/// Used to create a folder with the MediaController
/// </summary>
[DataContract]
public class PostedFolder
{
[DataMember(Name = "parentId")]
public string ParentId { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
}
}
@@ -20,7 +20,7 @@ namespace Umbraco.Web.Models.Mapping
config.CreateMap<UmbracoEntity, EntityBasic>()
.ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(x.NodeObjectTypeId), x.Key)))
.ForMember(basic => basic.Icon, expression => expression.MapFrom(entity => entity.ContentTypeIcon))
.ForMember(dto => dto.Trashed, expression => expression.Ignore())
.ForMember(dto => dto.Trashed, expression => expression.MapFrom(x => x.Trashed))
.ForMember(x => x.Alias, expression => expression.Ignore())
.AfterMap((entity, basic) =>
{
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using AutoMapper;
using Umbraco.Core;
@@ -28,7 +29,7 @@ namespace Umbraco.Web.Models.Mapping
public TabsAndPropertiesResolver(ILocalizedTextService localizedTextService, IEnumerable<string> ignoreProperties)
: this(localizedTextService)
{
{
if (ignoreProperties == null) throw new ArgumentNullException("ignoreProperties");
IgnoreProperties = ignoreProperties;
}
@@ -84,7 +85,7 @@ namespace Umbraco.Web.Models.Mapping
Alias = string.Format("{0}createdate", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
Label = localizedTextService.Localize("content/createDate"),
Description = localizedTextService.Localize("content/createDateDesc"),
Value = display.CreateDate.ToIsoString(),
Value = display.CreateDate.ToString(CultureInfo.CurrentCulture),
View = labelEditor
},
new ContentPropertyDisplay
@@ -92,7 +93,7 @@ namespace Umbraco.Web.Models.Mapping
Alias = string.Format("{0}updatedate", Constants.PropertyEditors.InternalGenericPropertiesPrefix),
Label = localizedTextService.Localize("content/updateDate"),
Description = localizedTextService.Localize("content/updateDateDesc"),
Value = display.UpdateDate.ToIsoString(),
Value = display.UpdateDate.ToString(CultureInfo.CurrentCulture),
View = labelEditor
}
};
@@ -132,8 +133,8 @@ namespace Umbraco.Web.Models.Mapping
switch (entityType)
{
case "content":
dtdId = Constants.System.DefaultContentListViewDataTypeId;
dtdId = Constants.System.DefaultContentListViewDataTypeId;
break;
case "media":
dtdId = Constants.System.DefaultMediaListViewDataTypeId;
@@ -146,7 +147,7 @@ namespace Umbraco.Web.Models.Mapping
}
//first try to get the custom one if there is one
var dt = dataTypeService.GetDataTypeDefinitionByName(customDtdName)
var dt = dataTypeService.GetDataTypeDefinitionByName(customDtdName)
?? dataTypeService.GetDataTypeDefinitionById(dtdId);
if (dt == null)
@@ -170,15 +171,15 @@ namespace Umbraco.Web.Models.Mapping
var listViewConfig = editor.PreValueEditor.ConvertDbToEditor(editor.DefaultPreValues, preVals);
//add the entity type to the config
listViewConfig["entityType"] = entityType;
listViewConfig["entityType"] = entityType;
//Override Tab Label if tabName is provided
if (listViewConfig.ContainsKey("tabName"))
{
var configTabName = listViewConfig["tabName"];
if (configTabName != null && string.IsNullOrWhiteSpace(configTabName.ToString()) == false)
listViewTab.Label = configTabName.ToString();
}
}
var listViewProperties = new List<ContentPropertyDisplay>();
listViewProperties.Add(new ContentPropertyDisplay
@@ -195,9 +196,9 @@ namespace Umbraco.Web.Models.Mapping
SetChildItemsTabPosition(display, listViewConfig, listViewTab);
}
private static void SetChildItemsTabPosition<TPersisted>(TabbedContentItem<ContentPropertyDisplay, TPersisted> display,
private static void SetChildItemsTabPosition<TPersisted>(TabbedContentItem<ContentPropertyDisplay, TPersisted> display,
IDictionary<string, object> listViewConfig,
Tab<ContentPropertyDisplay> listViewTab)
Tab<ContentPropertyDisplay> listViewTab)
where TPersisted : IContentBase
{
// Find position of tab from config
@@ -237,9 +238,9 @@ namespace Umbraco.Web.Models.Mapping
var groupsGroupsByName = content.PropertyGroups.OrderBy(x => x.SortOrder).GroupBy(x => x.Name);
foreach (var groupsByName in groupsGroupsByName)
{
var properties = new List<Property>();
// merge properties for groups with the same name
var properties = new List<Property>();
// merge properties for groups with the same name
foreach (var group in groupsByName)
{
var groupProperties = content.GetPropertiesForGroup(group)
@@ -281,8 +282,8 @@ namespace Umbraco.Web.Models.Mapping
tabs.Add(new Tab<ContentPropertyDisplay>
{
Id = 0,
Label = _localizedTextService.Localize("general/properties"),
Id = 0,
Label = _localizedTextService.Localize("general/properties"),
Alias = "Generic properties",
Properties = genericproperties
});
@@ -24,9 +24,8 @@ namespace Umbraco.Web.PropertyEditors
internal class TextboxPreValueEditor : PreValueEditor
{
[PreValueField("maxChars", "Maximum allowed characters", "number", Description = "If empty - no character limit")]
[PreValueField("maxChars", "Maximum allowed characters", "textstringlimited", Description = "If empty - 500 character limit")]
public bool MaxChars { get; set; }
}
}
}
}
+14 -1
View File
@@ -6,6 +6,7 @@ using System.Threading.Tasks;
using Umbraco.Core;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Sync;
namespace Umbraco.Web.Scheduling
{
@@ -20,9 +21,21 @@ namespace Umbraco.Web.Scheduling
_appContext = appContext;
}
private ILogger Logger { get { return _appContext.ProfilingLogger.Logger; } }
public override async Task<bool> PerformRunAsync(CancellationToken token)
{
if (_appContext == null) return true; // repeat...
switch (_appContext.GetCurrentServerRole())
{
case ServerRole.Slave:
Logger.Debug<ScheduledPublishing>("Does not run on slave servers.");
return true; // DO repeat, server role can change
case ServerRole.Unknown:
Logger.Debug<ScheduledPublishing>("Does not run on servers with unknown role.");
return true; // DO repeat, server role can change
}
// ensure we do not run if not main domain, but do NOT lock it
if (_appContext.MainDom.IsMainDom == false)
@@ -31,7 +44,7 @@ namespace Umbraco.Web.Scheduling
return false; // do NOT repeat, going down
}
using (DisposableTimer.DebugDuration<KeepAlive>(() => "Keep alive executing", () => "Keep alive complete"))
using (_appContext.ProfilingLogger.DebugDuration<KeepAlive>("Keep alive executing", "Keep alive complete"))
{
string umbracoAppUrl = null;
@@ -1,12 +1,18 @@
using System;
using System.Net.Http;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using System.Web.Hosting;
using umbraco;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Publishing;
using Umbraco.Core.Sync;
using Umbraco.Web.Mvc;
using Umbraco.Web.Routing;
using Umbraco.Web.Security;
namespace Umbraco.Web.Scheduling
{
@@ -23,7 +29,9 @@ namespace Umbraco.Web.Scheduling
_settings = settings;
}
public override async Task<bool> PerformRunAsync(CancellationToken token)
private ILogger Logger { get { return _appContext.ProfilingLogger.Logger; } }
public override bool PerformRun()
{
if (_appContext == null) return true; // repeat...
@@ -33,10 +41,10 @@ namespace Umbraco.Web.Scheduling
switch (_appContext.GetCurrentServerRole())
{
case ServerRole.Slave:
LogHelper.Debug<ScheduledPublishing>("Does not run on slave servers.");
Logger.Debug<ScheduledPublishing>("Does not run on slave servers.");
return true; // DO repeat, server role can change
case ServerRole.Unknown:
LogHelper.Debug<ScheduledPublishing>("Does not run on servers with unknown role.");
Logger.Debug<ScheduledPublishing>("Does not run on servers with unknown role.");
return true; // DO repeat, server role can change
}
@@ -47,72 +55,38 @@ namespace Umbraco.Web.Scheduling
return false; // do NOT repeat, going down
}
string umbracoAppUrl;
UmbracoContext tempContext = null;
try
{
umbracoAppUrl = _appContext == null || _appContext.UmbracoApplicationUrl.IsNullOrWhiteSpace()
? null
: _appContext.UmbracoApplicationUrl;
if (umbracoAppUrl.IsNullOrWhiteSpace())
// DO not run publishing if content is re-loading
if (content.Instance.isInitializing == false)
{
LogHelper.Warn<ScheduledPublishing>("No url for service (yet), skip.");
return true; // repeat
//TODO: We should remove this in v8, this is a backwards compat hack
// see notes in CacheRefresherEventHandler
// because notifications will not be sent if there is no UmbracoContext
// see NotificationServiceExtensions
var httpContext = new HttpContextWrapper(new HttpContext(new SimpleWorkerRequest("temp.aspx", "", new StringWriter())));
tempContext = UmbracoContext.EnsureContext(
httpContext,
_appContext,
new WebSecurity(httpContext, _appContext),
_settings,
UrlProviderResolver.Current.Providers,
true);
var publisher = new ScheduledPublisher(_appContext.Services.ContentService);
var count = publisher.CheckPendingAndProcess();
Logger.Debug<ScheduledPublishing>(() => string.Format("Processed {0} items", count));
}
}
catch (Exception e)
{
LogHelper.Error<ScheduledPublishing>("Could not acquire application url", e);
return true; // repeat
Logger.Error<ScheduledPublishing>("Failed (see exception).", e);
}
var url = umbracoAppUrl + "/RestServices/ScheduledPublish/Index";
using (DisposableTimer.DebugDuration<ScheduledPublishing>(
() => string.Format("Scheduled publishing executing @ {0}", url),
() => "Scheduled publishing complete"))
finally
{
try
{
using (var wc = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, url)
{
Content = new StringContent(string.Empty)
};
// running on a background task, requires its own (safe) scope
// (GetAuthenticationHeaderValue uses UserService to load the current user, hence requires a database)
// (might not need a scope but we don't know really)
using (var scope = ApplicationContext.Current.ScopeProvider.CreateScope())
{
//pass custom the authorization header
request.Headers.Authorization = AdminTokenAuthorizeAttribute.GetAuthenticationHeaderValue(_appContext);
scope.Complete();
}
var result = await wc.SendAsync(request, token);
var content = await result.Content.ReadAsStringAsync();
if (result.IsSuccessStatusCode)
{
LogHelper.Debug<ScheduledPublishing>(
() => string.Format(
"Request successfully sent to url = \"{0}\". ", url));
}
else
{
var msg = string.Format(
"Request failed with status code \"{0}\". Request content = \"{1}\".",
result.StatusCode, content);
var ex = new HttpRequestException(msg);
LogHelper.Error<ScheduledPublishing>(msg, ex);
}
}
}
catch (Exception e)
{
LogHelper.Error<ScheduledPublishing>(string.Format("Failed (at \"{0}\").", umbracoAppUrl), e);
}
if (tempContext != null)
tempContext.Dispose(); // nulls the ThreadStatic context
}
return true; // repeat
@@ -120,7 +94,7 @@ namespace Umbraco.Web.Scheduling
public override bool IsAsync
{
get { return true; }
get { return false; }
}
}
}
+3 -4
View File
@@ -116,9 +116,8 @@
<Reference Include="dotless.Core, Version=1.5.2.0, Culture=neutral, PublicKeyToken=96b446c9e63eae34, processorArchitecture=MSIL">
<HintPath>..\packages\dotless.1.5.2\lib\dotless.Core.dll</HintPath>
</Reference>
<Reference Include="Examine, Version=0.1.83.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.83\lib\net45\Examine.dll</HintPath>
<Private>True</Private>
<Reference Include="Examine, Version=0.1.88.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Examine.0.1.88\lib\net45\Examine.dll</HintPath>
</Reference>
<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>
@@ -361,6 +360,7 @@
<Compile Include="Models\ContentEditing\MemberTypeDisplay.cs" />
<Compile Include="Models\ContentEditing\MemberTypeSave.cs" />
<Compile Include="Models\ContentEditing\PostedFiles.cs" />
<Compile Include="Models\ContentEditing\PostedFolder.cs" />
<Compile Include="Models\ContentEditing\PropertyGroupBasic.cs" />
<Compile Include="Models\ContentEditing\PropertyTypeBasic.cs" />
<Compile Include="Models\ContentEditing\SimpleNotificationModel.cs" />
@@ -1981,7 +1981,6 @@
<Compile Include="WebServices\ExamineManagementApiController.cs" />
<Compile Include="WebServices\FolderBrowserService.cs" />
<Compile Include="WebServices\TagsController.cs" />
<Compile Include="WebServices\ScheduledPublishController.cs" />
<Compile Include="WebServices\UmbracoAuthorizedHttpHandler.cs" />
<Compile Include="WebServices\SaveFileController.cs" />
<Compile Include="WebServices\UmbracoAuthorizedWebService.cs">
+17 -5
View File
@@ -46,12 +46,10 @@ namespace Umbraco.Web
/// <param name="app"></param>
protected virtual void ConfigureMiddleware(IAppBuilder app)
{
//Ensure owin is configured for Umbraco back office authentication. If you have any front-end OWIN
// cookie configuration, this must be declared after it.
// Configure OWIN for authentication.
ConfigureUmbracoAuthentication(app);
app
.UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
.UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
.UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize)
.UseSignalR()
.FinalizeMiddlewareConfiguration();
}
@@ -68,6 +66,20 @@ namespace Umbraco.Web
Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider());
}
/// <summary>
/// Configure external/OAuth login providers
/// </summary>
/// <param name="app"></param>
protected virtual void ConfigureUmbracoAuthentication(IAppBuilder app)
{
// Ensure owin is configured for Umbraco back office authentication.
// Front-end OWIN cookie configuration must be declared after this code.
app
.UseUmbracoBackOfficeCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
.UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext, PipelineStage.Authenticate)
.UseUmbracoPreviewAuthentication(ApplicationContext, PipelineStage.Authorize);
}
/// <summary>
/// Raised when the middleware has been configured
/// </summary>
+1 -1
View File
@@ -70,7 +70,7 @@ namespace Umbraco.Web
if (!GlobalSettings.UseDirectoryUrls)
path += ".aspx";
else if (UmbracoConfig.For.UmbracoSettings().RequestHandler.AddTrailingSlash)
path += "/";
path = path.EnsureEndsWith("/");
}
path = ToAbsolute(path);
@@ -1,73 +0,0 @@
using System;
using System.Web.Mvc;
using umbraco;
using Umbraco.Core.Logging;
using Umbraco.Core.Publishing;
using Umbraco.Web.Mvc;
namespace Umbraco.Web.WebServices
{
/// <summary>
/// A REST controller used for running the scheduled publishing, this is called from the background worker timer
/// </summary>
[AdminTokenAuthorize]
public class ScheduledPublishController : UmbracoController
{
private static bool _isPublishingRunning = false;
[HttpPost]
public JsonResult Index()
{
if (_isPublishingRunning)
{
Logger.Debug<ScheduledPublishController>(() => "Scheduled publishing is currently executing this request will exit");
return null;
}
_isPublishingRunning = true;
try
{
// DO not run publishing if content is re-loading
if (content.Instance.isInitializing == false)
{
var publisher = new ScheduledPublisher(Services.ContentService);
var count = publisher.CheckPendingAndProcess();
Logger.Debug<ScheduledPublishController>(() => string.Format("The scheduler processed {0} items", count));
}
return Json(new
{
success = true
});
}
catch (Exception ee)
{
var errorMessage = "Error executing scheduled task";
if (HttpContext != null && HttpContext.Request != null)
{
if (HttpContext.Request.Url != null)
errorMessage = string.Format("{0} | Request to {1}", errorMessage, HttpContext.Request.Url);
if (HttpContext.Request.UserHostAddress != null)
errorMessage = string.Format("{0} | Coming from {1}", errorMessage, HttpContext.Request.UserHostAddress);
if (HttpContext.Request.UrlReferrer != null)
errorMessage = string.Format("{0} | Referrer {1}", errorMessage, HttpContext.Request.UrlReferrer);
}
LogHelper.Error<ScheduledPublishController>(errorMessage, ee);
Response.StatusCode = 400;
return Json(new
{
success = false,
message = ee.Message
});
}
finally
{
_isPublishingRunning = false;
}
}
}
}
+1 -1
View File
@@ -3,7 +3,7 @@
<package id="AutoMapper" version="3.3.1" targetFramework="net45" />
<package id="ClientDependency" version="1.9.2" targetFramework="net45" />
<package id="dotless" version="1.5.2" targetFramework="net45" />
<package id="Examine" version="0.1.83" targetFramework="net45" />
<package id="Examine" version="0.1.88" targetFramework="net45" />
<package id="HtmlAgilityPack" version="1.4.9.5" targetFramework="net45" />
<package id="Lucene.Net" version="2.9.4.1" targetFramework="net45" />
<package id="Markdown" version="1.14.7" targetFramework="net45" />
@@ -70,10 +70,11 @@ namespace umbraco.presentation.umbraco.dialogs
private void import_Click(object sender, EventArgs e)
{
var xd = new XmlDocument();
xd.XmlResolver = null;
xd.Load(tempFile.Value);
var userId = base.getUser().Id;
var element = XElement.Parse(xd.InnerXml);
var importContentTypes = ApplicationContext.Current.Services.PackagingService.ImportContentTypes(element, userId);
var contentType = importContentTypes.FirstOrDefault();
@@ -104,7 +105,8 @@ namespace umbraco.presentation.umbraco.dialogs
documentTypeFile.PostedFile.SaveAs(fileName);
var xd = new XmlDocument();
xd.Load(fileName);
xd.XmlResolver = null;
xd.Load(fileName);
dtName.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Name").FirstChild.Value;
dtAlias.Text = xd.DocumentElement.SelectSingleNode("//DocumentType/Info/Alias").FirstChild.Value;
@@ -26,7 +26,7 @@ namespace umbraco.dialogs
protected void Page_Load(object sender, EventArgs e)
{
Button1.Text = ui.Text("update");
pane_form.Text = ui.Text("notifications", "editNotifications", node.Text, base.getUser());
pane_form.Text = ui.Text("notifications", "editNotifications", Server.HtmlEncode(node.Text), base.getUser());
}
#region Web Form Designer generated code

Some files were not shown because too many files have changed in this diff Show More