Compare commits

..

2 Commits

Author SHA1 Message Date
Mads Rasmussen bca29975dd get the create date from the variant 2018-08-10 13:31:14 +02:00
Mads Rasmussen be8bd3d443 emit event on variant change + update the publish state on the info tab 2018-08-10 13:07:37 +02:00
3413 changed files with 136249 additions and 108527 deletions
+1 -11
View File
@@ -27,14 +27,4 @@ dotnet_naming_symbols.private_fields.applicable_kinds = field
dotnet_naming_symbols.private_fields.applicable_accessibilities = private
dotnet_naming_style.prefix_underscore.capitalization = camel_case
dotnet_naming_style.prefix_underscore.required_prefix = _
# https://github.com/MicrosoftDocs/visualstudio-docs/blob/master/docs/ide/editorconfig-code-style-settings-reference.md
[*.cs]
csharp_style_var_for_built_in_types = true:suggestion
csharp_style_var_when_type_is_apparent = true:suggestion
csharp_style_var_elsewhere = true:suggestion
csharp_prefer_braces = false : none
[*.{js,less}]
trim_trailing_whitespace = false
dotnet_naming_style.prefix_underscore.required_prefix = _
-66
View File
@@ -1,66 +0,0 @@
---
name: 🐛 Bug Report
about: File a bug report, if you've discovered a problem in Umbraco.
---
A brief description of the issue goes here.
<!--
If you haven't yet done so, please read the "contributing guidelines"
thoroughly. Then, proceed by filling out the rest of the details in the issue
template below. The more details you can give us, the easier it will be for us
to determine the cause of a problem.
See: https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md
-->
Reproduction
------------
If you're filing a bug, please describe how to reproduce it. Include as much
relevant information as possible, such as:
### Bug summary
<!--
* Write a short summary of the bug
* Try to pinpoint it as much as possible
* Try to state the _actual problem_, and not just what you _think_ the
solution might be.
-->
### Specifics
<!--
* Mention the URL where this bug occurs, if applicable
* What version of Umbraco are you using (down to the very last digit!)
* What browser and version you are using
* Please mention if you've checked it in other browsers as well
* Please include *full error messages* and *screenshots* if possible
-->
### Steps to reproduce
<!--
* Clearly mention the steps to reproduce the bug
-->
### Expected result
<!--
* What did you _expect_ that would happen on your Umbraco site?
* Describe the intended/desired outcome after you did the steps mentioned.
-->
### Actual result
<!--
* What is the actual result of the above steps?
* Describe the behaviour of the bug
* Please, please include **error messages** and screenshots. They might mean
nothing to you, but they are _very_ helpful to us.
-->
@@ -1,31 +0,0 @@
---
name: 📮 Feature Request
about: Open a feature request, if you want to propose a new feature.
---
A brief description of your feature request goes here.
<!--
If you want to discuss the feature you're imagining, please make sure to keep
those discussions on the forum at https://our.umbraco.com/ (choose the
category "Contributing to Umbraco", Umbraco HQ follows all new topics there).
Once you've come to a conclusion in the discussion, feel free to propose the
new feature here.
-->
How can you help?
-------------------------------
<!--
The resources (read: available time and effort) of Umbraco's core team are
limited.
If we can not work on your suggestion, please don't take it personally. Most
likely, it's either:
- We think your idea is valid, but we can't find the time to work on it.
- Your idea might be better suited as a package, if it's not suitable for
the majority of users.
-->
@@ -1,9 +0,0 @@
---
name: ⁉️ Support Question
about: Having trouble with Umbraco? -> https://our.umbraco.com
---
This issue tracker is NOT meant for support questions. If you have a question,
please join us on the forum at https://our.umbraco.com.
Thanks!
@@ -1,9 +0,0 @@
---
name: 📖 Documentation Issue
about: See https://github.com/umbraco/UmbracoDocs/issues for documentation issues
---
The Umbraco documentation has its own dedicated repository. Please open your
documentation-related issue at https://github.com/umbraco/UmbracoDocs/issues
Thanks!
@@ -1,31 +0,0 @@
---
name: 🔐 Security Issue
about: Discovered a Security Issue in Umbraco?
---
⚠️ PLEASE DON'T DISCLOSE SECURITY-RELATED ISSUES PUBLICLY, SEE BELOW.
If you have found a security issue in Umbraco, please send the details to
security@umbraco.com and don't disclose it publicly until we can provide a fix for
it. If you wish, we'll credit you for finding verified issues, when we release
the patched version.
❗ Please read more about how to report security issues on https://umbraco.com/security
A note on "Self XSS"
--------------------
Umbraco is a CMS, that allows users to edit content on a website. As such,
all _authenticated users_ can:
- Edit content, and (depending on the field types) insert HTML and CSS in that
content, with a variety of allowed attributes.
- Depending on the user level: Edit template files, and insert C#, HTML, CSS and
javascript in so on.
- Upload files to the site, which will become publicly available.
We see these functionalities as _features_, and not as security issues. Please
report the mentioned items only if they can be performed by non-authorized
users, or other exploitable vulnerabilities.
Thanks!
-12
View File
@@ -1,12 +0,0 @@
### Prerequisites
- [ ] I have added steps to test this contribution in the description below
If there's an existing issue for this PR then this fixes: <!-- link to the issue here! -->
### Description
<!-- A description of the changes proposed in the pull-request and how to test these changes -->
<!-- Thanks for contributing to Umbraco CMS! -->
-62
View File
@@ -1,62 +0,0 @@
_You are browsing the Umbraco v8 branch. Umbraco 8 is currently under development._
[![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS) to go to the v7 branch._
_Ready to try out Version 8? [See the quick start guide](V8_GETTING_STARTED.md)._
When is Umbraco 8 coming?
=========================
When it's ready. We're done with the major parts of the architecture work and are focusing on three separate tracks to prepare Umbraco 8 for release:
1) Editor Track (_currently in progress_). Without editors, there's no market for Umbraco. So we want to make sure that Umbraco 8 is full of love for editors.
2) Partner Track. Without anyone implementing Umbraco, there's nothing for editors to update. So we want to make sure that Umbraco 8 is a joy to implement
3) Contributor Track. Without our fabulous ecosystem of both individual Umbracians and 3rd party ISVs, Umbraco wouldn't be as rich a platform as it is today. We want to make sure that it's easy, straight forward and as backwards-compatible as possible to create packages for Umbraco
Once a track is done, we start releasing previews where we ask people to test the features we believe are ready. While the testing is going on and we gather feedback, we move on to the next track. This doesn't mean that there hasn't already been work done in the area, but that a track focuses on finalizing, polishing and preparing the features for release.
Umbraco CMS
===========
The friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 443,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
[![ScreenShot](img/vimeo.png)](https://vimeo.com/172382998/)
## Umbraco CMS
Umbraco is a free open source Content Management System built on the ASP.NET platform. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
## Watch an introduction video
[![ScreenShot](https://shop.umbraco.com/images/whatisumbraco.png)](https://umbraco.tv/videos/umbraco-v7/content-editor/basics/introduction/cms-explanation/)
## Umbraco - The Friendly CMS
For the first time on the Microsoft platform, there is a free user- and developer-friendly CMS that makes it quick and easy to create websites - and a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, right out of the box.
Umbraco is not only loved by developers, but is a content editor's dream. Enjoy intuitive editing tools, media management, responsive views, and approval workflows to send your content live.
Used by more than 443,000 active websites including Carlsberg, Segway, Amazon and Heinz and **The Official ASP.NET and IIS.NET website from Microsoft** ([https://asp.net](https://asp.net) / [https://iis.net](https://iis.net)), you can be sure that the technology is proven, stable and scalable. Backed by the team at Umbraco HQ, and supported by a dedicated community of over 220,000 craftspeople globally, you can trust that Umbraco is a safe choice and is here to stay.
To view more examples, please visit [https://umbraco.com/case-studies-testimonials/](https://umbraco.com/case-studies-testimonials/)
## Why Open Source?
As an Open Source platform, Umbraco is more than just a CMS. We are transparent with our roadmap for future versions, our incremental sprint planning notes are publicly accessible, and community contributions and packages are available for all to use.
## Trying out Umbraco CMS
[Umbraco Cloud](https://umbraco.com/cloud) is the easiest and fastest way to use Umbraco yet, with full support for all your custom .NET code and integrations. You're up and running in less than a minute, and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14-day trial, no credit card needed.
If you want to DIY, you can [download Umbraco](https://our.umbraco.com/download) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host it yourself, and handling deployments and upgrades will be all up to you.
## Community
Our friendly community is available 24/7 at the community hub we call ["Our Umbraco"](https://our.umbraco.com). Our Umbraco features forums for questions and answers, documentation, downloadable plugins for Umbraco, and a rich collection of community resources.
## Contribute to Umbraco
Umbraco is contribution-focused and community-driven. If you want to contribute back to Umbraco, please check out our [guide to contributing](CONTRIBUTING.md).
## Found a bug?
Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report refer to our [online guide for reporting issues](CONTRIBUTING_DETAILED.md#reporting-bugs).
You can comment and report issues on the [github issue tracker](https://github.com/umbraco/Umbraco-CMS/issues).
Since [September 2018](https://umbraco.com/blog/a-second-take-on-umbraco-issue-tracker-hello-github-issues/), the old issue tracker is in read-only mode, but can still be found at [http://issues.umbraco.org](http://issues.umbraco.org).
+158 -163
View File
@@ -1,163 +1,158 @@
#
# Umbraco Cms Git Ignore
#
# common files
*.obj
*.pdb
*.user
*.aps
*.pch
*.vspscc
*.orig
*.suo
*.vs10x
*.ndproj
*.ignorer.*
# common directories
.DS_Store
._.DS_Store
.vs/
/local/
# build directories
[Bb]in/
[Db]ebug*/
[Rr]elease*/
obj/
# tools
_ReSharper*/
_NCrunch_*/
*.ncrunchsolution
*.ncrunchsolution.user
*.ncrunchproject
*.crunchsolution.cache
tools/NDepend/
[Tt]est[Rr]esult*
[Bb]uild[Ll]og.*
*.[Pp]ublish.xml
[sS]ource
[sS]andbox
umbraco.config
App_Data/TEMP/*
[Uu]mbraco/[Pp]resentation/[Uu]mbraco/[Pp]lugins/*
[Uu]mbraco/[Pp]resentation/[Uu]ser[Cc]ontrols/*
[Uu]mbraco/[Pp]resentation/[Ss]cripts/*
[Uu]mbraco/[Pp]resentation/[Ff]onts/*
[Uu]mbraco/[Pp]resentation/[Cc]ss/*
src/Umbraco.Web.UI/[Cc]ss/*
src/Umbraco.Web.UI/App_Code/*
src/Umbraco.Web.UI/App_Data/*
src/Umbraco.Tests/App_Data/*
src/Umbraco.Web.UI/[Mm]edia/*
src/Umbraco.Web.UI/[Mm]aster[Pp]ages/*
src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/*
!src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/[Ww]eb.[Cc]onfig
src/Umbraco.Web.UI/[Xx]slt/*
src/Umbraco.Web.UI/[Ii]mages/*
src/Umbraco.Web.UI/[Ss]cripts/*
src/Umbraco.Web.UI/Web.*.config.transformed
umbraco/presentation/umbraco/plugins/uComponents/uComponentsInstaller.ascx
umbraco/presentation/packages/uComponents/MultiNodePicker/CustomTreeService.asmx
src/Umbraco.Tests/config/applications.config
src/Umbraco.Tests/config/trees.config
src/Umbraco.Web.UI/web.config
src/Umbraco.Web.UI/[Cc]onfig/ClientDependency.config
src/Umbraco.Web.UI/[Cc]onfig/serilog.config
src/Umbraco.Web.UI/[Cc]onfig/serilog.user.config
src/Umbraco.Tests/config/404handlers.config
src/Umbraco.Web.UI/[Vv]iews/*.cshtml
src/Umbraco.Web.UI/[Vv]iews/*.vbhtml
src/Umbraco.Tests/[Cc]onfig/umbracoSettings.config
src/Umbraco.Web.UI/[Vv]iews/*
src/packages/
src/packages/repositories.config
src/Umbraco.Web.UI/[Ww]eb.config
*.transformed
node_modules
lib-bower
src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/umbraco.*
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/*.loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/init.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/routes.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.dev.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.dev.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/canvasdesigner.*.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.css
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
src/Umbraco.Web.UI/[Uu]mbraco/[Aa]ssets/*
src/Umbraco.Web.UI.Client/[Bb]uild/*
src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/
src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/
src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
src/Umbraco.Web.UI.Client/vwd.webinfo
src/Umbraco.Web.UI/App_Plugins/*
src/*.psess
src/*.vspx
NDependOut/*
QueryResult.htm
src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
src/Umbraco.Web.UI/[Uu]mbraco/plugins/*
build/ApiDocs/*
build/ApiDocs/Output/*
src/Umbraco.Web.UI.Client/bower_components/*
/src/Umbraco.Web.UI/[Uu]mbraco/preview
/src/Umbraco.Web.UI/[Uu]mbraco/preview.old
# ignore rule for clearing out Belle (avoid rebuilding all the time)
preserve.belle
#Ignore Rule for output of generated documentation files from Grunt docserve
src/Umbraco.Web.UI.Client/docs/api
src/*.boltdata/
src/umbraco.sln.ide/*
src/.vs/
src/Umbraco.Web.UI/[Jj]s/*
src/Umbraco.Tests/[Mm]edia
tools/docfx/*
apidocs/_site/*
src/*/project.lock.json
src/.idea/*
apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
src/packages/
src/PrecompiledWeb/*
# build
build.out/
build.tmp/
build/hooks/
build/temp/
# eof
/src/Umbraco.Web.UI.Client/TESTS-*.xml
#
# Umbraco Cms Git Ignore
#
# common files
*.obj
*.pdb
*.user
*.aps
*.pch
*.vspscc
*.orig
*.suo
*.vs10x
*.ndproj
# common directories
.DS_Store
._.DS_Store
.vs/
/local/
# build directories
[Bb]in/
[Db]ebug*/
[Rr]elease*/
obj/
# tools
_ReSharper*/
_NCrunch_*/
*.ncrunchsolution
*.ncrunchsolution.user
*.ncrunchproject
*.crunchsolution.cache
tools/NDepend/
[Tt]est[Rr]esult*
[Bb]uild[Ll]og.*
*.[Pp]ublish.xml
[sS]ource
[sS]andbox
umbraco.config
App_Data/TEMP/*
[Uu]mbraco/[Pp]resentation/[Uu]mbraco/[Pp]lugins/*
[Uu]mbraco/[Pp]resentation/[Uu]ser[Cc]ontrols/*
[Uu]mbraco/[Pp]resentation/[Ss]cripts/*
[Uu]mbraco/[Pp]resentation/[Ff]onts/*
[Uu]mbraco/[Pp]resentation/[Cc]ss/*
src/Umbraco.Web.UI/[Cc]ss/*
src/Umbraco.Web.UI/App_Code/*
src/Umbraco.Web.UI/App_Data/*
src/Umbraco.Tests/App_Data/*
src/Umbraco.Web.UI/[Mm]edia/*
src/Umbraco.Web.UI/[Mm]aster[Pp]ages/*
src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/*
!src/Umbraco.Web.UI/[Mm]acro[Ss]cripts/[Ww]eb.[Cc]onfig
src/Umbraco.Web.UI/[Xx]slt/*
src/Umbraco.Web.UI/[Ii]mages/*
src/Umbraco.Web.UI/[Ss]cripts/*
src/Umbraco.Web.UI/Web.*.config.transformed
umbraco/presentation/umbraco/plugins/uComponents/uComponentsInstaller.ascx
umbraco/presentation/packages/uComponents/MultiNodePicker/CustomTreeService.asmx
src/Umbraco.Tests/config/applications.config
src/Umbraco.Tests/config/trees.config
src/Umbraco.Web.UI/web.config
src/Umbraco.Web.UI/[Cc]onfig/ClientDependency.config
src/Umbraco.Tests/config/404handlers.config
src/Umbraco.Web.UI/[Vv]iews/*.cshtml
src/Umbraco.Web.UI/[Vv]iews/*.vbhtml
src/Umbraco.Tests/[Cc]onfig/umbracoSettings.config
src/Umbraco.Web.UI/[Vv]iews/*
src/packages/
src/packages/repositories.config
src/Umbraco.Web.UI/[Ww]eb.config
*.transformed
node_modules
lib-bower
src/Umbraco.Web.UI/[Uu]mbraco/[Ll]ib/*
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/umbraco.*
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/*.loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/init.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/routes.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.dev.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/loader.dev.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/app.js
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/canvasdesigner.*.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.css
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.html
src/Umbraco.Web.UI/[Uu]mbraco/[Aa]ssets/*
src/Umbraco.Web.UI.Client/[Bb]uild/*
src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/
src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/
src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
src/Umbraco.Web.UI/App_Plugins/*
src/*.psess
src/*.vspx
NDependOut/*
QueryResult.htm
src/Umbraco.Web.UI/[Cc]onfig/appSettings.config
src/Umbraco.Web.UI/[Cc]onfig/connectionStrings.config
src/Umbraco.Web.UI/[Uu]mbraco/plugins/*
build/ApiDocs/*
build/ApiDocs/Output/*
src/Umbraco.Web.UI.Client/bower_components/*
/src/Umbraco.Web.UI/[Uu]mbraco/preview
/src/Umbraco.Web.UI/[Uu]mbraco/preview.old
# ignore rule for clearing out Belle (avoid rebuilding all the time)
preserve.belle
#Ignore Rule for output of generated documentation files from Grunt docserve
src/Umbraco.Web.UI.Client/docs/api
src/*.boltdata/
src/umbraco.sln.ide/*
src/.vs/
src/Umbraco.Web.UI/[Jj]s/*
src/Umbraco.Tests/[Mm]edia
tools/docfx/*
apidocs/_site/*
src/*/project.lock.json
src/.idea/*
apidocs/api/*
build/docs.zip
build/ui-docs.zip
build/csharp-docs.zip
src/packages/
src/PrecompiledWeb/*
# build
build.out/
build.tmp/
build/hooks/
build/temp/
# eof
+1 -1
View File
@@ -1,6 +1,6 @@
# The MIT License (MIT) #
Copyright (c) 2013-present Umbraco
Copyright (c) 2013 Umbraco
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+20 -20
View File
@@ -3,57 +3,57 @@
[string]$Directory
)
$workingDirectory = $Directory
CD "$($workingDirectory)"
CD $workingDirectory
# Clone repo
$fullGitUrl = "https://$($env:GIT_URL)/$($env:GIT_REPOSITORYNAME).git"
git clone $($fullGitUrl) $($env:GIT_REPOSITORYNAME) 2>&1 | % { $_.ToString() }
$fullGitUrl = "https://$env:GIT_URL/$env:GIT_REPOSITORYNAME.git"
git clone $fullGitUrl 2>&1 | % { $_.ToString() }
# Remove everything so that unzipping the release later will update everything
# Don't remove the readme file nor the git directory
Write-Host "Cleaning up git directory before adding new version"
Remove-Item -Recurse "$($workingDirectory)\$($env:GIT_REPOSITORYNAME)\*" -Exclude README.md,.git
Remove-Item -Recurse $workingDirectory\$env:GIT_REPOSITORYNAME\* -Exclude README.md,.git
# Find release zip
$zipsDir = "$($workingDirectory)\$($env:BUILD_DEFINITIONNAME)\zips"
$zipsDir = "$workingDirectory\$env:BUILD_DEFINITIONNAME\zips"
$pattern = "UmbracoCms.([0-9]{1,2}.[0-9]{1,3}.[0-9]{1,3}).zip"
Write-Host "Searching for Umbraco release files in $($zipsDir) for a file with pattern $($pattern)"
$file = (Get-ChildItem "$($zipsDir)" | Where-Object { $_.Name -match "$($pattern)" })
Write-Host "Searching for Umbraco release files in $workingDirectory\$zipsDir for a file with pattern $pattern"
$file = (Get-ChildItem $zipsDir | Where-Object { $_.Name -match "$pattern" })
if($file)
{
# Get release name
$version = [regex]::Match($($file.Name), $($pattern)).captures.groups[1].value
$releaseName = "Umbraco $($version)"
Write-Host "Found $($releaseName)"
$version = [regex]::Match($file.Name, $pattern).captures.groups[1].value
$releaseName = "Umbraco $version"
Write-Host "Found $releaseName"
# Unzip into repository to update release
Add-Type -AssemblyName System.IO.Compression.FileSystem
Write-Host "Unzipping $($file.FullName) to $($workingDirectory)\$($env:GIT_REPOSITORYNAME)"
[System.IO.Compression.ZipFile]::ExtractToDirectory("$($file.FullName)", "$($workingDirectory)\$($env:GIT_REPOSITORYNAME)")
Write-Host "Unzipping $($file.FullName) to $workingDirectory\$env:GIT_REPOSITORYNAME"
[System.IO.Compression.ZipFile]::ExtractToDirectory("$($file.FullName)", "$workingDirectory\$env:GIT_REPOSITORYNAME")
# Telling git who we are
git config --global user.email "coffee@umbraco.com" 2>&1 | % { $_.ToString() }
git config --global user.name "Umbraco HQ" 2>&1 | % { $_.ToString() }
# Commit
CD "$($workingDirectory)\$($env:GIT_REPOSITORYNAME)"
Write-Host "Committing Umbraco $($version) Release from Build Output"
CD $env:GIT_REPOSITORYNAME
Write-Host "Committing Umbraco $version Release from Build Output"
git add . 2>&1 | % { $_.ToString() }
git commit -m " Release $($releaseName) from Build Output" 2>&1 | % { $_.ToString() }
git commit -m " Release $releaseName from Build Output" 2>&1 | % { $_.ToString() }
# Tag the release
git tag -a "v$($version)" -m "v$($version)"
git tag -a "v$version" -m "v$version"
# Push release to master
$fullGitAuthUrl = "https://$($env:GIT_USERNAME):$($GitHubPersonalAccessToken)@$($env:GIT_URL)/$($env:GIT_REPOSITORYNAME).git"
git push $($fullGitAuthUrl) 2>&1 | % { $_.ToString() }
$fullGitAuthUrl = "https://$($env:GIT_USERNAME):$GitHubPersonalAccessToken@$env:GIT_URL/$env:GIT_REPOSITORYNAME.git"
git push $fullGitAuthUrl 2>&1 | % { $_.ToString() }
#Push tag to master
git push $($fullGitAuthUrl) --tags 2>&1 | % { $_.ToString() }
git push $fullGitAuthUrl --tags 2>&1 | % { $_.ToString() }
}
else
{
Write-Error "Umbraco release file not found, searched in $($workingDirectory)\$($zipsDir) for a file with pattern $($pattern) - canceling"
Write-Error "Umbraco release file not found, searched in $workingDirectory\$zipsDir for a file with pattern $pattern - cancelling"
}
+81 -54
View File
@@ -1,60 +1,87 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms.Core</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>https://umbraco.com/dist/nuget/logo-small.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<metadata minClientVersion="3.4.4">
<id>UmbracoCms.Core</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains the core assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms-package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<!--
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="AutoMapper" version="[7.0.1,7.999999)" />
<dependency id="ClientDependency" version="[1.9.6,1.999999)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0,1.999999)" />
<dependency id="CSharpTest.Net.Collections" version="[14.906.1403.1082,14.999999)" />
<dependency id="Examine" version="[1.0.0-beta025,1.999999)" />
<dependency id="HtmlAgilityPack" version="[1.7.2,1.999999)" />
<dependency id="ImageProcessor" version="[2.6.1.19,2.999999)" />
<dependency id="ImageProcessor.Web" version="[4.9.2.19,4.999999)" />
<dependency id="ImageProcessor.Web.Config" version="[2.4.1.19,2.999999)" />
<dependency id="LightInject" version="[5.1.2,5.999999)" />
<dependency id="LightInject.Annotation" version="[1.1.0,1.999999)" />
<dependency id="LightInject.Mvc" version="[2.0.0,2.999999)" />
<dependency id="LightInject.WebApi" version="[2.0.0,2.999999)" />
<dependency id="log4net" version="[2.0.8,2.999999)" />
<dependency id="Log4Net.Async" version="[2.0.4,2.999999)" />
<dependency id="Lucene.Net.Contrib" version="[3.0.3,3.999999)" />
<dependency id="Markdown" version="[2.2.1,2.999999)" />
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.1,2.999999)" />
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.6,5.999999)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.3,2.999999)" />
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.6,5.999999)" />
<dependency id="Microsoft.CodeAnalysis.CSharp" version="[2.8.0,2.999999)" />
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[1.0.8,1.999999)" />
<dependency id="Microsoft.Net.Compilers" version="[2.8.0,2.999999)" />
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[4.0.0,4.999999)" />
<dependency id="Microsoft.Owin.Security.Cookies" version="[4.0.0,4.999999)" />
<dependency id="Microsoft.Owin.Security.OAuth" version="[4.0.0,4.999999)" />
<dependency id="Microsoft.Web.Xdt" version="[2.1.2,2.999999)" />
<dependency id="MiniProfiler" version="[3.2.0.157,3.999999)" />
<dependency id="MySql.Data" version="[6.10.7,6.999999)" />
<dependency id="Newtonsoft.Json" version="[11.0.2,11.999999)" />
<dependency id="NPoco" version="[3.9.3,3.999999)" />
<dependency id="Semver" version="[2.0.4,2.999999)" />
<dependency id="System.Reflection.Primitives" version="[4.3.0,4.999999)" />
<dependency id="System.Runtime.Handles" version="[4.3.0,4.999999)" />
<dependency id="System.Security.Cryptography.X509Certificates" version="[4.3.2,4.999999)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.8.0,4.999999)" />
<dependency id="Umbraco.SqlServerCE" version="[4.0.0.1,4.999999)" />
</dependencies>
</metadata>
<files>
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.dll" target="lib\net472\Umbraco.Core.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.dll" target="lib\net472\Umbraco.Web.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net472\Umbraco.Web.UI.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Examine.dll" target="lib\net472\Umbraco.Examine.dll" />
<group targetFramework="net472">
<!--
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="AutoMapper" version="[8.0.0,8.999999)" />
<dependency id="LightInject" version="[5.4.0,5.999999)" />
<dependency id="LightInject.Annotation" version="[1.1.0,1.999999)" />
<dependency id="LightInject.Web" version="[2.0.0,2.999999)" />
<dependency id="Microsoft.AspNet.Identity.Core" version="[2.2.2,2.999999)" />
<dependency id="Microsoft.AspNet.WebApi.Client" version="[5.2.7,5.999999)" />
<dependency id="Microsoft.Owin" version="[4.0.1,4.999999)" />
<dependency id="MiniProfiler" version="[4.0.138,4.999999)" />
<dependency id="Newtonsoft.Json" version="[12.0.1,12.999999)" />
<dependency id="Semver" version="[2.0.4,2.999999)" />
<dependency id="Serilog" version="[2.8.0,2.999999)" />
<dependency id="Serilog.Enrichers.Process" version="[2.0.1,2.999999)" />
<dependency id="Serilog.Enrichers.Thread" version="[3.0.0,3.999999)" />
<dependency id="Serilog.Filters.Expressions" version="[2.0.0,2.999999)" />
<dependency id="Serilog.Formatting.Compact" version="[1.0.0,1.999999)" />
<dependency id="Serilog.Formatting.Compact.Reader" version="[1.0.3,1.999999)" />
<dependency id="Serilog.Settings.AppSettings" version="[2.2.2,2.999999)" />
<dependency id="Serilog.Sinks.File" version="[4.0.0,4.999999)" />
<dependency id="Umbraco.SqlServerCE" version="[4.0.0.1,4.999999)" />
<dependency id="NPoco" version="[3.9.4,3.999999)" />
</group>
</dependencies>
</metadata>
<files>
<!-- libs -->
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.dll" target="lib\net472\Umbraco.Core.dll" />
<!-- docs -->
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.xml" target="lib\Umbraco.Core.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.xml" target="lib\Umbraco.Web.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.xml" target="lib\Umbraco.Web.UI.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Examine.xml" target="lib\Umbraco.Examine.xml" />
<!-- docs -->
<file src="$BuildTmp$\WebApp\bin\Umbraco.Core.xml" target="lib\Umbraco.Core.xml" />
<file src="tools\install.core.ps1" target="tools\install.ps1" />
<!-- symbols -->
<file src="$BuildTmp$\bin\Umbraco.Core.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Core\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Core" />
</files>
<!-- symbols -->
<file src="$BuildTmp$\bin\Umbraco.Core.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Core\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Core" />
<file src="$BuildTmp$\bin\Umbraco.Web.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Web\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web" />
<file src="$BuildTmp$\bin\Umbraco.Web.UI.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Web.UI\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web.UI" />
<file src="$BuildTmp$\bin\Umbraco.Examine.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Examine\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Examine" />
</files>
</package>
-66
View File
@@ -1,66 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms.Web</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>https://umbraco.com/dist/nuget/logo-small.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Contains the web assemblies needed to run Umbraco Cms. This package only contains assemblies and can be used for package development. Use the UmbracoCms package to setup Umbraco in Visual Studio as an ASP.NET project.</description>
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<group targetFramework="net472">
<!--
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="UmbracoCms.Core" version="[$version$]" />
<dependency id="ClientDependency" version="[1.9.7,1.999999)" />
<dependency id="ClientDependency-Mvc5" version="[1.8.0,1.999999)" />
<dependency id="CSharpTest.Net.Collections" version="[14.906.1403.1082,14.999999)" />
<dependency id="Examine" version="[1.0.0,1.999999)" />
<dependency id="HtmlAgilityPack" version="[1.8.14,1.999999)" />
<dependency id="ImageProcessor" version="[2.7.0.100,2.999999)" />
<dependency id="LightInject.Mvc" version="[2.0.0,2.999999)" />
<dependency id="LightInject.WebApi" version="[2.0.0,2.999999)" />
<dependency id="Markdown" version="[2.2.1,2.999999)" />
<dependency id="Microsoft.AspNet.Identity.Owin" version="[2.2.2,2.999999)" />
<dependency id="Microsoft.AspNet.Mvc" version="[5.2.7,5.999999)" />
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.4.0,2.999999)" />
<dependency id="Microsoft.AspNet.WebApi" version="[5.2.7,5.999999)" />
<dependency id="Microsoft.Owin.Host.SystemWeb" version="[4.0.1,4.999999)" />
<dependency id="Microsoft.Owin.Security.Cookies" version="[4.0.1,4.999999)" />
<dependency id="Microsoft.Owin.Security.OAuth" version="[4.0.1,4.999999)" />
<dependency id="System.Threading.Tasks.Dataflow" version="[4.9.0,4.999999)" />
</group>
</dependencies>
</metadata>
<files>
<!-- libs -->
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.dll" target="lib\net472\Umbraco.Web.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.dll" target="lib\net472\Umbraco.Web.UI.dll" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Examine.dll" target="lib\net472\Umbraco.Examine.dll" />
<!-- docs -->
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.xml" target="lib\Umbraco.Web.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.xml" target="lib\Umbraco.Web.UI.xml" />
<file src="$BuildTmp$\WebApp\bin\Umbraco.Examine.xml" target="lib\Umbraco.Examine.xml" />
<!-- symbols -->
<file src="$BuildTmp$\bin\Umbraco.Web.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Web\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Web" />
<file src="$BuildTmp$\bin\Umbraco.Examine.pdb" target="lib" />
<file src="$BuildTmp$\..\src\Umbraco.Examine\**\*.cs" exclude="$BuildTmp$\..\src\**\TemporaryGeneratedFile*.cs" target="src\Umbraco.Examine" />
</files>
</package>
+53 -62
View File
@@ -1,68 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="3.4.4">
<id>UmbracoCms</id>
<version>8.0.0</version>
<title>Umbraco Cms</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>https://umbraco.com/dist/nuget/logo-small.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Installs Umbraco Cms in your Visual Studio ASP.NET project</description>
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET project</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<metadata minClientVersion="3.4.4">
<id>UmbracoCms</id>
<version>8.0.0</version>
<title>Umbraco Cms</title>
<authors>Umbraco HQ</authors>
<owners>Umbraco HQ</owners>
<licenseUrl>http://opensource.org/licenses/MIT</licenseUrl>
<projectUrl>http://umbraco.com/</projectUrl>
<iconUrl>http://umbraco.com/media/357769/100px_transparent.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Installs Umbraco Cms in your Visual Studio ASP.NET project</description>
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET project</summary>
<language>en-US</language>
<tags>umbraco</tags>
<dependencies>
<dependency id="UmbracoCms.Core" version="[$version$]" />
<!--
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.2.3, 2.999999)" />
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.0.0-alpha.20]" />
</dependencies>
</metadata>
<files>
<file src="$BuildTmp$\Configs\**" target="Content\Config" exclude="$BuildTmp$\Configs\Web.config.transform" />
<file src="$BuildTmp$\WebApp\Views\**" target="Content\Views" exclude="$BuildTmp$\WebApp\Views\Web.config" />
<file src="$BuildTmp$\WebApp\default.aspx" target="Content\default.aspx" />
<file src="$BuildTmp$\WebApp\Global.asax" target="Content\Global.asax" />
<file src="$BuildTmp$\WebApp\Web.config" target="UmbracoFiles\Web.config" />
<file src="$BuildTmp$\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
<file src="$BuildTmp$\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
<file src="$BuildTmp$\WebApp\config\BackOfficeTours\**" target="Content\Config\BackOfficeTours" />
<file src="$BuildTmp$\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
<file src="$BuildTmp$\WebApp\umbraco_client\**" target="UmbracoFiles\umbraco_client" />
<file src="$BuildTmp$\WebApp\Media\Web.config" target="Content\Media\Web.config" />
<group targetFramework="net472">
<file src="$BuildTmp$\WebApp\App_Plugins\ModelsBuilder\modelsbuilder.htm" target="Content\App_Plugins\ModelsBuilder\modelsbuilder.htm" />
<file src="$BuildTmp$\WebApp\App_Plugins\ModelsBuilder\modelsbuilder.controller.js" target="Content\App_Plugins\ModelsBuilder\modelsbuilder.controller.js" />
<file src="$BuildTmp$\WebApp\App_Plugins\ModelsBuilder\modelsbuilder.resource.js" target="Content\App_Plugins\ModelsBuilder\modelsbuilder.resource.js" />
<file src="$BuildTmp$\WebApp\App_Plugins\ModelsBuilder\package.manifest" target="Content\App_Plugins\ModelsBuilder\package.manifest" />
<dependency id="UmbracoCms.Web" version="[$version$]" />
<!--
note: dependencies are specified as [x.y.z,x.999999) eg [2.1.0,2.999999) and NOT [2.1.0,3.0.0) because
the latter would pick anything below 3.0.0 and that includes prereleases such as 3.0.0-alpha, and we do
not want this to happen as the alpha of the next major is, really, the next major already.
-->
<dependency id="Microsoft.AspNet.SignalR.Core" version="[2.4.0, 2.999999)" />
<dependency id="Umbraco.ModelsBuilder.Ui" version="[8.0.0-alpha.38]" />
<dependency id="ImageProcessor.Web" version="[4.10.0.100,4.999999)" />
<dependency id="ImageProcessor.Web.Config" version="[2.5.0.100,2.999999)" />
<dependency id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform" version="[2.0.1,2.999999)" />
<dependency id="Microsoft.Net.Compilers" version="[2.10.0,2.999999)" />
<file src="tools\install.ps1" target="tools\install.ps1" />
<file src="tools\Readme.txt" target="tools\Readme.txt" />
<file src="tools\ReadmeUpgrade.txt" target="tools\ReadmeUpgrade.txt" />
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
<file src="tools\applications.config.install.xdt" target="Content\config\applications.config.install.xdt" />
<file src="tools\ClientDependency.config.install.xdt" target="Content\config\ClientDependency.config.install.xdt" />
<file src="tools\Dashboard.config.install.xdt" target="Content\config\Dashboard.config.install.xdt" />
<file src="tools\trees.config.install.xdt" target="Content\config\trees.config.install.xdt" />
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" />
</group>
</dependencies>
</metadata>
<files>
<!-- files -->
<file src="$BuildTmp$\Configs\**" target="Content\Config" exclude="$BuildTmp$\Configs\Web.config.transform" />
<file src="$BuildTmp$\WebApp\Views\**" target="Content\Views" exclude="$BuildTmp$\WebApp\Views\Web.config" />
<file src="$BuildTmp$\WebApp\Global.asax" target="Content\Global.asax" />
<file src="$BuildTmp$\WebApp\config\BackOfficeTours\**" target="Content\Config\BackOfficeTours" />
<file src="$BuildTmp$\WebApp\Media\Web.config" target="Content\Media\Web.config" />
<!-- these files are copied by install.ps1 -->
<file src="$BuildTmp$\WebApp\Web.config" target="UmbracoFiles\Web.config" />
<file src="$BuildTmp$\WebApp\umbraco\**" target="UmbracoFiles\umbraco" />
<file src="$BuildTmp$\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
<!-- tools -->
<!-- beware! install.ps1 not supported by PackageReference -->
<file src="tools\install.ps1" target="tools\install.ps1" />
<file src="tools\Readme.txt" target="tools\Readme.txt" />
<file src="tools\ReadmeUpgrade.txt" target="tools\ReadmeUpgrade.txt" />
<!-- config transforms -->
<!-- beware! config transforms not supported by PackageReference -->
<file src="tools\Web.config.install.xdt" target="Content\Web.config.install.xdt" />
<file src="tools\ClientDependency.config.install.xdt" target="Content\config\ClientDependency.config.install.xdt" />
<file src="tools\umbracoSettings.config.install.xdt" target="Content\config\umbracoSettings.config.install.xdt" />
<file src="tools\Views.Web.config.install.xdt" target="Views\Web.config.install.xdt" /> <!-- FIXME: Content\ !! and then... transform?! -->
<!-- UmbracoCms props and targets -->
<file src="build\**" target="build" />
</files>
<file src="build\**" target="build" />
</files>
</package>
+3
View File
@@ -47,6 +47,9 @@
<CustomFilesToInclude Include=".\umbraco\**\*">
<Dir>umbraco</Dir>
</CustomFilesToInclude>
<CustomFilesToInclude Include=".\umbraco_client\**\*">
<Dir>umbraco_client</Dir>
</CustomFilesToInclude>
<CustomFilesToInclude Include=".\Global.asax">
<Dir>.</Dir>
</CustomFilesToInclude>
@@ -0,0 +1,95 @@
<?xml version="1.0"?>
<dashBoard xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<section alias="StartupSettingsDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Welcome" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
<control showOnce="true" addPanel="true" panelCaption="" xdt:Transform="InsertIfMissing">
views/dashboard/settings/settingsdashboardintro.html
</control>
</tab>
</section>
<section alias="StartupFormsDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<areas>
<area>forms</area>
</areas>
<tab caption="Install Umbraco Forms">
<control showOnce="true" addPanel="true" panelCaption="">
views/dashboard/forms/formsdashboardintro.html
</control>
</tab>
</section>
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<areas xdt:Transform="InsertIfMissing">
<area xdt:Transform="InsertIfMissing">developer</area>
</areas>
</section>
<section alias="StartupDeveloperDashboardSection">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Replace">
<control showOnce="true" addPanel="true" panelCaption="">
views/dashboard/developer/developerdashboardvideos.html
</control>
</tab>
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
</section>
<section alias="StartupDeveloperDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Examine Management" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing">
<control>
views/dashboard/developer/examinemanagement.html
</control>
</tab>
</section>
<section alias="StartupMediaDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Content" xdt:Locator="Match(caption)" xdt:Transform="InsertIfMissing" />
<tab caption="Content" xdt:Locator="Match(caption)" xdt:Transform="Replace">
<control showOnce="false" addPanel="false" panelCaption="">
views/dashboard/media/mediafolderbrowser.html
</control>
</tab>
</section>
<section alias="StartupMemberDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<tab caption="Get Started" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Get Started" xdt:Transform="Insert">
<control showOnce="true" addPanel="true" panelCaption="">
views/dashboard/members/membersdashboardvideos.html
</control>
</tab>
</section>
<section alias="uGoLiveDashboardSection" xdt:Locator="Match(alias)" xdt:Transform="Remove" />
<section alias="ExamineManagement" xdt:Locator="Match(alias)" xdt:Transform="Remove" />
<section alias="StartupDashboardSection">
<tab caption="Change Password" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
<tab caption="Last Edits" xdt:Locator="Match(caption)" xdt:Transform="Remove" />
</section>
<section alias="RedirectUrlManagement" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<areas>
<area>content</area>
</areas>
<tab caption="Redirect URL Management">
<control>
views/dashboard/developer/redirecturls.html
</control>
</tab>
</section>
<section alias="UmbracoHealthCheck" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing">
<areas>
<area>developer</area>
</areas>
<tab caption="Health Check">
<control>
views/dashboard/developer/healthcheck.html
</control>
</tab>
</section>
</dashBoard>
+11 -11
View File
@@ -1,26 +1,26 @@
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
----------------------------------------------------
Don't forget to build!
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
file (and config files in the config folder).
When upgrading your website using NuGet you should answer "No" to the questions to overwrite the Web.config
file (and config files in the config folder).
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
Publish this won't affect you.
The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, config\splashes and global.asax.
The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, umbraco_client, config\splashes and global.asax.
Please read the release notes on our.umbraco.com:
https://our.umbraco.com/download/releases
http://our.umbraco.com/contribute/releases
- Umbraco
- Umbraco
+20 -11
View File
@@ -1,30 +1,39 @@
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
----------------------------------------------------
*** IMPORTANT NOTICE FOR UPGRADES FROM VERSIONS BELOW 7.7.0 ***
Be sure to read the version specific upgrade information before proceeding:
https://our.umbraco.com/documentation/Getting-Started/Setup/Upgrading/version-specific#version-7-7-0
Depending on the version you are upgrading from, you may need to make some changes to your web.config
and you will need to be aware of the breaking changes listed there to see if these affect your installation.
Don't forget to build!
We've done our best to transform your configuration files but in case something is not quite right: remember we
backed up your files in App_Data\NuGetBackup so you can find the original files before they were transformed.
We've overwritten all the files in the Umbraco folder, these have been backed up in
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or
a package might have added. Only the existing files were overwritten. If you customized anything then make
We've overwritten all the files in the Umbraco and Umbraco_Client folder, these have been backed up in
App_Data\NuGetBackup. We didn't overwrite the UI.xml file nor did we remove any files or folders that you or
a package might have added. Only the existing files were overwritten. If you customized anything then make
sure to do a compare and merge with the NuGetBackup folder.
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
This NuGet package includes build targets that extend the creation of a deploy package, which is generated by
Publishing from Visual Studio. The targets will only work once Publishing is configured, so if you don't use
Publish this won't affect you.
The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, config\splashes and global.asax.
The following items will now be automatically included when creating a deploy package or publishing to the file
system: umbraco, umbraco_client, config\splashes and global.asax.
Please read the release notes on our.umbraco.com:
http://our.umbraco.com/contribute/releases
- Umbraco
- Umbraco
+27 -4
View File
@@ -2,11 +2,17 @@
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<configSections xdt:Transform="InsertIfMissing" />
<configSections>
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<section name="FileSystemProviders" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<section name="ExamineLuceneIndexSets" type="Umbraco.Examine.Config.IndexSets, Umbraco.Examine" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="SetAttributes(type,requirePermission)" />
<sectionGroup name="applicationSettings" xdt:Locator="Match(name)">
<section name="umbraco.presentation.Properties.Settings" xdt:Locator="Match(name)" xdt:Transform="Remove" />
</sectionGroup>
<sectionGroup name="system.web.webPages.razor" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<sectionGroup name="umbracoConfiguration" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing">
<section name="settings" type="Umbraco.Core.Configuration.UmbracoSettings.UmbracoSettingsSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="dashBoard" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<section name="FileSystemProviders" type="Umbraco.Core.Configuration.FileSystemProvidersSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="dashBoard" type="Umbraco.Core.Configuration.Dashboard.DashboardSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
<section name="HealthChecks" type="Umbraco.Core.Configuration.HealthChecks.HealthChecksSection, Umbraco.Core" requirePermission="false" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
</sectionGroup>
</configSections>
@@ -16,18 +22,26 @@
<add key="owin:appStartup" value="UmbracoDefaultOwinStartup" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
<add key="Umbraco.ModelsBuilder.Enable" value="false" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
<add key="Umbraco.ModelsBuilder.ModelsMode" value="Nothing" xdt:Locator="Match(key)" xdt:Transform="InsertIfMissing" />
<add key="umbracoDefaultUILanguage" value="en-US" xdt:Locator="Match(key)" xdt:Transform="SetAttributes(value)" />
</appSettings>
<umbracoConfiguration xdt:Transform="InsertIfMissing">
<settings configSource="config\umbracoSettings.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<dashBoard configSource="config\Dashboard.config" xdt:Locator="Match(configSource)" xdt:Transform="Remove" />
<FileSystemProviders configSource="config\FileSystemProviders.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<dashBoard configSource="config\Dashboard.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
<HealthChecks configSource="config\HealthChecks.config" xdt:Locator="Match(configSource)" xdt:Transform="InsertIfMissing" />
</umbracoConfiguration>
<FileSystemProviders xdt:Transform="Remove" />
<system.data xdt:Transform="InsertIfMissing">
<DbProviderFactories xdt:Transform="InsertIfMissing">
<remove invariant="System.Data.SqlServerCe.4.0" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
<add name="Microsoft SQL Server Compact Data Provider 4.0" invariant="System.Data.SqlServerCe.4.0" description=".NET Framework Data Provider for Microsoft SQL Server Compact" type="System.Data.SqlServerCe.SqlCeProviderFactory, System.Data.SqlServerCe" xdt:Locator="Match(invariant)" xdt:Transform="SetAttributes(invariant,description,type)" />
<remove invariant="MySql.Data.MySqlClient" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
<add invariant="MySql.Data.MySqlClient" xdt:Locator="Match(invariant)" xdt:Transform="Remove" />
<add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" xdt:Locator="Match(invariant)" xdt:Transform="InsertIfMissing" />
<add invariant="MySql.Data.MySqlClient" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data" xdt:Locator="Match(invariant)" xdt:Transform="SetAttributes(type)" />
</DbProviderFactories>
</system.data>
<clientDependency xdt:Transform="RemoveAttributes(version)" />
@@ -35,9 +49,14 @@
<system.web xdt:Transform="InsertIfMissing" />
<system.web>
<siteMap xdt:Transform="Remove" />
<siteMap xdt:Transform="InsertIfMissing">
<providers xdt:Transform="InsertIfMissing">
<remove name="MySqlSiteMapProvider" xdt:Transform="InsertIfMissing" />
</providers>
</siteMap>
<httpRuntime xdt:Transform="InsertIfMissing" />
<httpRuntime maxRequestLength="51200" fcnMode="Single" xdt:Transform="SetAttributes(fcnMode,maxRequestLength)" />
<httpRuntime targetFramework="4.7.2" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
<httpRuntime targetFramework="4.5" xdt:Locator="Condition(count(@targetFramework) != 1)" xdt:Transform="SetAttributes(targetFramework)" />
<membership defaultProvider="DefaultMembershipProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove" />
<roleManager defaultProvider="DefaultRoleProvider" xdt:Locator="Match(defaultProvider)" xdt:Transform="Remove"/>
@@ -131,6 +150,10 @@
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="log4net" publicKeyToken="669e0ddf0bb1aa2a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-2.0.8.0" newVersion="2.0.8.0" />
</dependentAssembly>
<dependentAssembly xdt:Transform="Insert">
<assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.0.0.0" newVersion="4.0.0.0" />
@@ -1,11 +1,11 @@
<?xml version="1.0"?>
<applications xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<add alias="content" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="media" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="settings" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="developer" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="users" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="member" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="forms" name="Forms" sortOrder="6" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing" />
<add alias="translation" sortOrder="7" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon,sortOrder)" />
</applications>
<add alias="content" icon="traycontent" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="media" icon="traymedia" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="settings" icon="traysettings" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="developer" icon="traydeveloper" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="users" icon="trayusers" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="member" icon="traymember" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon)" />
<add alias="forms" name="Forms" icon="icon-umb-contour" sortOrder="6" xdt:Locator="Match(alias)" xdt:Transform="InsertIfMissing" />
<add alias="translation" icon="traytranslation" sortOrder="7" xdt:Locator="Match(alias)" xdt:Transform="SetAttributes(icon,sortOrder)" />
</applications>
+2
View File
@@ -33,6 +33,7 @@ if ($project) {
robocopy $umbracoBinFolder $umbracoBinBackupPath /e /LOG:$copyLogsPath\UmbracoBinBackup.log
# Delete files Umbraco ships with
if(Test-Path $umbracoBinFolder\log4net.dll) { Remove-Item $umbracoBinFolder\log4net.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll) { Remove-Item $umbracoBinFolder\Microsoft.ApplicationBlocks.Data.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll) { Remove-Item $umbracoBinFolder\System.Data.SqlServerCe.Entity.dll -Force -Confirm:$false }
@@ -73,6 +74,7 @@ if ($project) {
if(Test-Path $umbracoBinFolder\Microsoft.Web.Helpers.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Helpers.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll) { Remove-Item $umbracoBinFolder\Microsoft.Web.Mvc.FixedDisplayModes.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MiniProfiler.dll) { Remove-Item $umbracoBinFolder\MiniProfiler.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\MySql.Data.dll) { Remove-Item $umbracoBinFolder\MySql.Data.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Newtonsoft.Json.dll) { Remove-Item $umbracoBinFolder\Newtonsoft.Json.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Owin.dll) { Remove-Item $umbracoBinFolder\Owin.dll -Force -Confirm:$false }
if(Test-Path $umbracoBinFolder\Semver.dll) { Remove-Item $umbracoBinFolder\Semver.dll -Force -Confirm:$false }
+47 -2
View File
@@ -44,6 +44,14 @@ if ($project) {
robocopy $umbracoFolder $umbracoBackupPath /e /LOG:$copyLogsPath\UmbracoBackup.log
robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
$umbracoClientFolder = Join-Path $projectPath "Umbraco_Client"
New-Item -ItemType Directory -Force -Path $umbracoClientFolder
$umbracoClientFolderSource = Join-Path $installPath "UmbracoFiles\Umbraco_Client"
$umbracoClientBackupPath = Join-Path $backupPath "Umbraco_Client"
New-Item -ItemType Directory -Force -Path $umbracoClientBackupPath
robocopy $umbracoClientFolder $umbracoClientBackupPath /e /LOG:$copyLogsPath\UmbracoClientBackup.log
robocopy $umbracoClientFolderSource $umbracoClientFolder /is /it /e /LOG:$copyLogsPath\UmbracoClientCopy.log
$copyWebconfig = $true
$destinationWebConfig = Join-Path $projectPath "Web.config"
@@ -54,7 +62,7 @@ if ($project) {
[xml]$config = Get-Content $destinationWebConfig
$config.configuration.appSettings.ChildNodes | ForEach-Object {
if($_.key -eq "Umbraco.Core.ConfigurationStatus")
if($_.key -eq "umbracoConfigurationStatus")
{
# The web.config has an umbraco-specific appSetting in it
# don't overwrite it and let config transforms do their thing
@@ -77,6 +85,18 @@ if ($project) {
$splashesDestination = Join-Path $projectPath "Config\splashes\"
New-Item $splashesDestination -Type directory
Copy-Item $splashesSource $splashesDestination -Force
$sqlCe64Source = Join-Path $installPath "UmbracoFiles\bin\amd64\*"
$sqlCe64Destination = Join-Path $projectPath "bin\amd64\"
Copy-Item $sqlCe64Source $sqlCe64Destination -Force
$sqlCex86Source = Join-Path $installPath "UmbracoFiles\bin\x86\*"
$sqlCex86Destination = Join-Path $projectPath "bin\x86\"
Copy-Item $sqlCex86source $sqlCex86Destination -Force
$umbracoUIXMLSource = Join-Path $installPath "UmbracoFiles\Umbraco\Config\Create\UI.xml"
$umbracoUIXMLDestination = Join-Path $projectPath "Umbraco\Config\Create\UI.xml"
Copy-Item $umbracoUIXMLSource $umbracoUIXMLDestination -Force
} else {
# This part only runs for upgrades
@@ -98,6 +118,31 @@ if ($project) {
{
# Not a big problem if this fails, let it go
}
Try
{
$uiXmlConfigPath = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
$uiXmlFile = Join-Path $umbracoFolder -ChildPath "Config" | Join-Path -ChildPath "create" | Join-Path -ChildPath "UI.xml"
$uiXml = New-Object System.Xml.XmlDocument
$uiXml.PreserveWhitespace = $true
$uiXml.Load($uiXmlFile)
$createExists = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks/create")
if($createExists.Count -eq 0)
{
$macrosTasksNode = $uiXml.SelectNodes("//nodeType[@alias='macros']/tasks")
#Creating: <create assembly="umbraco" type="macroTasks" />
$createNode = $uiXml.CreateElement("create")
$createNode.SetAttribute("assembly", "umbraco")
$createNode.SetAttribute("type", "macroTasks")
$macrosTasksNode.AppendChild($createNode)
$uiXml.Save($uiXmlFile)
}
}
Catch { }
}
$installFolder = Join-Path $projectPath "Install"
@@ -114,4 +159,4 @@ if ($project) {
{
$DTE.ItemOperations.OpenFile($toolsPath + '\ReadmeUpgrade.txt')
}
}
}
@@ -0,0 +1,20 @@
<?xml version="1.0"?>
<log4net xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<appender name="AsynchronousLog4NetAppender" xdt:Transform="Remove" xdt:Locator="Match(name)" />
<appender name="AsynchronousLog4NetAppender" type="Umbraco.Core.Logging.ParallelForwardingAppender,Umbraco.Core" xdt:Transform="Insert" >
<appender-ref ref="rollingFile" />
</appender>
<appender name="rollingFile" type="log4net.Appender.RollingFileAppender" xdt:Transform="InsertIfMissing">
<file value="App_Data\Logs\UmbracoTraceLog.txt" />
<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
<appendToFile value="true" />
<rollingStyle value="Date" />
<maximumFileSize value="5MB" />
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value=" %date [P%property{processId}/D%property{appDomainId}/T%thread] %-5level %logger - %message%newline" />
</layout>
<encoding value="utf-8" />
</appender>
</log4net>
@@ -0,0 +1,143 @@
<?xml version="1.0"?>
<trees xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<!--Content-->
<add alias="content" application="content"
xdt:Locator="Match(application,alias)"
xdt:Transform="RemoveAttributes(silent)" />
<add initialize="false" sortOrder="0" alias="contentRecycleBin" application="content" title="Recycle Bin" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.cms.presentation.Trees.ContentRecycleBin, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add initialize="true" sortOrder="0" alias="content" application="content" title="Content" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.ContentTreeController, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<!--Media-->
<add initialize="true" sortOrder="0" alias="media" application="media" title="Media" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.MediaTreeController, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add initialize="false" sortOrder="0" alias="mediaRecycleBin" application="media" title="Recycle Bin" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.cms.presentation.Trees.MediaRecycleBin, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<!--Settings-->
<add application="settings" alias="nodeTypes"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add initialize="true" sortOrder="0" alias="documentTypes" application="settings" title="Document Types" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.ContentTypeTreeController, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="settings" alias="stylesheets" title="Stylesheets" type="umbraco.loadStylesheets, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="3"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="stylesheetProperty" title="Stylesheet Property" type="umbraco.loadStylesheetProperty, Umbraco.Web" iconClosed="" iconOpen="" initialize="false" sortOrder="0"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="templates" title="Templates" type="Umbraco.Web.Trees.TemplatesTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="1"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" alias="partialViews" type="Umbraco.Web.Trees.PartialViewsTree, Umbraco.Web"
xdt:Locator="Match(application,alias,type)"
xdt:Transform="Remove()" />
<add application="settings" alias="partialViews" title="Partial Views" silent="false" initialize="true" iconClosed="icon-folder" iconOpen="icon-folder" type="Umbraco.Web.Trees.PartialViewsTreeController, Umbraco.Web" sortOrder="2"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="settings" alias="scripts" title="Scripts" type="Umbraco.Web.Trees.ScriptTreeController, Umbraco.Web" 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.Web.Trees.DictionaryTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="8"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add alias="dictionary" application="settings"
xdt:Locator="Match(application,alias)"
xdt:Transform="RemoveAttributes(action)" />
<add application="settings" alias="languages" title="Languages" type="Umbraco.Web.Trees.LanguageTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="5"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="settings" initialize="true" alias="mediaTypes" title="Media Types" type="Umbraco.Web.Trees.MediaTypeTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open" sortOrder="7"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<!--Developer-->
<add alias="packager" application="developer"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add alias="packagerPackages" application="developer"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add initialize="true" sortOrder="0" alias="packager" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.PackagesTreeController, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<!-- Before 7.4 this tree had the alias 'dataType', without the 's' on the end, this is here to rename it -->
<add sortOrder="1" alias="dataTypes" application="developer" type="Umbraco.Web.Trees.DataTypeTreeController, Umbraco.Web"
xdt:Locator="Match(application,type)"
xdt:Transform="SetAttributes(alias,sortOrder)" />
<!-- Yes, set the sortOrder again, like above because.. sometimes apparently we already have a dataTypes node and we can't remove more than one.. if they're equal though (same alias,application and sortOrder) it doesn't throw an error -->
<add sortOrder="1" alias="dataTypes" application="developer"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes(sortOrder)" />
<add initialize="true" sortOrder="2" alias="macros" application="developer" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MacroTreeController, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="relationTypes" title="Relation Types" type="umbraco.loadRelationTypes, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder" sortOrder="4"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTree, Umbraco.Web"
xdt:Locator="Match(application,alias,type)"
xdt:Transform="Remove()" />
<add application="developer" alias="partialViewMacros" type="Umbraco.Web.Trees.PartialViewMacrosTreeController, Umbraco.Web" silent="false" initialize="true" sortOrder="6" title="Partial View Macro Files" iconClosed="icon-folder" iconOpen="icon-folder"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="developer" alias="python"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<!--Users-->
<add initialize="true" sortOrder="0" alias="users" application="users" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.UserTreeController, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="users" alias="userTypes"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add application="users" alias="userPermissions"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<!--Members-->
<add initialize="true" sortOrder="0" alias="member" application="member" title="Members" iconClosed="icon-folder" iconOpen="icon-folder-open" type="Umbraco.Web.Trees.MemberTreeController, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add application="member" alias="memberGroup"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add application="member" sortOrder="2" alias="memberGroups" title="Member Groups" type="umbraco.loadMemberGroups, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<add application="member" alias="memberType"
xdt:Locator="Match(application,alias)"
xdt:Transform="Remove" />
<add application="member" sortOrder="1" alias="memberTypes" initialize="true" title="Member Types" type="Umbraco.Web.Trees.MemberTypeTreeController, Umbraco.Web" iconClosed="icon-folder" iconOpen="icon-folder-open"
xdt:Locator="Match(application,alias)"
xdt:Transform="InsertIfMissing" />
<!--Translation-->
<add silent="false" initialize="true" sortOrder="1" alias="openTasks" application="translation" title="Tasks assigned to you" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.loadOpenTasks, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add alias="openTasks" application="translation"
xdt:Locator="Match(application,alias)"
xdt:Transform="RemoveAttributes(action)" />
<add silent="false" initialize="true" sortOrder="2" alias="yourTasks" application="translation" title="Tasks created by you" iconClosed="icon-folder" iconOpen="icon-folder" type="umbraco.loadYourTasks, Umbraco.Web"
xdt:Locator="Match(application,alias)"
xdt:Transform="SetAttributes()" />
<add alias="yourTasks" application="translation"
xdt:Locator="Match(application,alias)"
xdt:Transform="RemoveAttributes(action)" />
</trees>
+50 -55
View File
@@ -43,6 +43,14 @@
$release = "" + $semver.Major + "." + $semver.Minor + "." + $semver.Patch
Write-Host "Update UmbracoVersion.cs"
$this.ReplaceFileText("$($this.SolutionRoot)\src\Umbraco.Core\Configuration\UmbracoVersion.cs", `
"(\d+)\.(\d+)\.(\d+)(.(\d+))?", `
"$release")
$this.ReplaceFileText("$($this.SolutionRoot)\src\Umbraco.Core\Configuration\UmbracoVersion.cs", `
"CurrentComment => `"(.+)`"", `
"CurrentComment => `"$($semver.PreRelease)`"")
Write-Host "Update IIS Express port in csproj"
$updater = New-Object "Umbraco.Build.ExpressPortUpdater"
$csproj = "$($this.SolutionRoot)\src\Umbraco.Web.UI\Umbraco.Web.UI.csproj"
@@ -61,9 +69,6 @@
$global:node_nodepath = $this.ClearEnvVar("NODEPATH")
$global:node_npmcache = $this.ClearEnvVar("NPM_CONFIG_CACHE")
$global:node_npmprefix = $this.ClearEnvVar("NPM_CONFIG_PREFIX")
# https://github.com/gruntjs/grunt-contrib-connect/issues/235
$this.SetEnvVar("NODE_NO_HTTP2", "1")
})
$ubuild.DefineMethod("RestoreNode",
@@ -73,8 +78,6 @@
$this.SetEnvVar("NODEPATH", $node_nodepath)
$this.SetEnvVar("NPM_CONFIG_CACHE", $node_npmcache)
$this.SetEnvVar("NPM_CONFIG_PREFIX", $node_npmprefix)
$ignore = $this.ClearEnvVar("NODE_NO_HTTP2")
})
$ubuild.DefineMethod("CompileBelle",
@@ -92,40 +95,48 @@
# so we have to take care of it else they'll bubble and kill the build
if ($error.Count -gt 0) { return }
try {
Push-Location "$($this.SolutionRoot)\src\Umbraco.Web.UI.Client"
Write-Output "" > $log
Push-Location "$($this.SolutionRoot)\src\Umbraco.Web.UI.Client"
Write-Output "" > $log
Write-Output "### node version is:" > $log
node -v >> $log 2>&1
if (-not $?) { throw "Failed to report node version." }
Write-Output "### node version is:" > $log
&node -v >> $log 2>&1
if (-not $?) { throw "Failed to report node version." }
Write-Output "### npm version is:" >> $log 2>&1
npm -v >> $log 2>&1
if (-not $?) { throw "Failed to report npm version." }
Write-Output "### npm version is:" >> $log 2>&1
&npm -v >> $log 2>&1
if (-not $?) { throw "Failed to report npm version." }
Write-Output "### clean npm cache" >> $log 2>&1
npm cache clean --force >> $log 2>&1
$error.Clear() # that one can fail 'cos security bug - ignore
Write-Output "### clean npm cache" >> $log 2>&1
&npm cache clean --force >> $log 2>&1
$error.Clear() # that one can fail 'cos security bug - ignore
Write-Output "### npm install" >> $log 2>&1
npm install >> $log 2>&1
Write-Output ">> $? $($error.Count)" >> $log 2>&1
# Don't really care about the messages from npm install making us think there are errors
$error.Clear()
Write-Output "### npm install" >> $log 2>&1
&npm install >> $log 2>&1
Write-Output ">> $? $($error.Count)" >> $log 2>&1
Write-Output "### gulp build for version $($this.Version.Release)" >> $log 2>&1
npx gulp build --buildversion=$this.Version.Release >> $log 2>&1
if (-not $?) { throw "Failed to build" } # that one is expected to work
} finally {
Pop-Location
Write-Output "### install bower" >> $log 2>&1
&npm install -g bower >> $log 2>&1
$error.Clear() # that one fails 'cos bower is deprecated - ignore
# FIXME: should we filter the log to find errors?
#get-content .\build.tmp\belle.log | %{ if ($_ -match "build") { write $_}}
Write-Output "### install gulp" >> $log 2>&1
&npm install -g gulp >> $log 2>&1
$error.Clear() # that one fails 'cos deprecated stuff - ignore
# restore
$this.RestoreNode()
}
Write-Output "### install gulp-cli" >> $log 2>&1
&npm install -g gulp-cli --quiet >> $log 2>&1
if (-not $?) { throw "Failed to install gulp-cli" } # that one is expected to work
Write-Output "### gulp build for version $($this.Version.Release)" >> $log 2>&1
&gulp build --buildversion=$this.Version.Release >> $log 2>&1
if (-not $?) { throw "Failed to build" } # that one is expected to work
Pop-Location
# fixme - should we filter the log to find errors?
#get-content .\build.tmp\belle.log | %{ if ($_ -match "build") { write $_}}
# restore
$this.RestoreNode()
# setting node_modules folder to hidden
# used to prevent VS13 from crashing on it while loading the websites project
@@ -177,7 +188,7 @@
{
Write-Host "Prepare Tests"
# FIXME: - idea is to avoid rebuilding everything for tests
# fixme - idea is to avoid rebuilding everything for tests
# but because of our weird assembly versioning (with .* stuff)
# everything gets rebuilt all the time...
#Copy-Files "$tmp\bin" "." "$tmp\tests"
@@ -330,6 +341,9 @@
$ubuild.DefineMethod("PrepareBuild",
{
Write-Host "Clear folders and files"
$this.RemoveDirectory("$($this.SolutionRoot)\src\Umbraco.Web.UI.Client\bower_components")
$this.TempStoreFile("$($this.SolutionRoot)\src\Umbraco.Web.UI\web.config")
Write-Host "Create clean web.config"
$this.CopyFile("$($this.SolutionRoot)\src\Umbraco.Web.UI\web.Template.config", "$($this.SolutionRoot)\src\Umbraco.Web.UI\web.config")
@@ -385,15 +399,9 @@
-Symbols -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmscore.log"
if (-not $?) { throw "Failed to pack NuGet UmbracoCms.Core." }
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Web.nuspec" `
-Properties BuildTmp="$($this.BuildTemp)" `
-Version "$($this.Version.Semver.ToString())" `
-Symbols -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmsweb.log"
if (-not $?) { throw "Failed to pack NuGet UmbracoCms.Web." }
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.nuspec" `
-Properties BuildTmp="$($this.BuildTemp)" `
-Version "$($this.Version.Semver.ToString())" `
-Version $this.Version.Semver.ToString() `
-Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cms.log"
if (-not $?) { throw "Failed to pack NuGet UmbracoCms." }
@@ -409,7 +417,7 @@
$ubuild.DefineMethod("VerifyNuGet",
{
$this.VerifyNuGetConsistency(
("UmbracoCms", "UmbracoCms.Core", "UmbracoCms.Web"),
("UmbracoCms", "UmbracoCms.Core"),
("Umbraco.Core", "Umbraco.Web", "Umbraco.Web.UI", "Umbraco.Examine"))
if ($this.OnError()) { return }
})
@@ -449,20 +457,6 @@
if ($this.OnError()) { return }
$this.PrepareAzureGallery()
if ($this.OnError()) { return }
$this.PostPackageHook()
if ($this.OnError()) { return }
Write-Host "Done"
})
$ubuild.DefineMethod("PostPackageHook",
{
# run hook
if ($this.HasMethod("PostPackage"))
{
Write-Host "Run PostPackage hook"
$this.PostPackage();
if (-not $?) { throw "Failed to run hook." }
}
})
# ################################################################
@@ -478,4 +472,5 @@
$ubuild.Build()
if ($ubuild.OnError()) { return }
}
Write-Host "Done"
if ($get) { return $ubuild }
View File
View File
@@ -17,7 +17,7 @@ Assume positive intent and try to understand before being understood.
Treat others as you would like to be treated.
This also goes for treating the HQ with respect. For example: dont promote products on [our.umbraco.com](https://our.umbraco.com) that directly compete with our commercial offerings which enables us to work for a sustainable Umbraco.
This also goes for treating the HQ with respect. For example: dont promote products on [our.umbraco.org](https://our.umbraco.org) that directly compete with our commercial offerings which enables us to work for a sustainable Umbraco.
## Open
@@ -1,4 +1,4 @@
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/temp8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/temp8/docs/CONTRIBUTING.md) to go to the v8 branch_
# Contributing to Umbraco CMS
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
@@ -16,9 +16,9 @@ This document gives you a quick overview on how to get started, we will link to
## Guidelines for contributions we welcome
Not all changes are wanted, so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valuable time.
Not all changes are wanted so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valueable time.
We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md). Make sure to talk to us before making large changes.
We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md), make sure to talk to us before making large changes.
Remember, if an issue is in the `Up for grabs` list or you've asked for some feedback before you sent us a PR, your PR will not be closed as unwanted.
@@ -36,7 +36,7 @@ Great question! The short version goes like this:
* **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md)
* **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions)
* **Commit** - done? Yay! 🎉 It is recommended to create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`
* **Commit** - done? Yay! 🎉 It is recommended to create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-U4-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `U4-12345`
* **Push** - great, now you can push the changes up to your fork on GitHub
* **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go.
@@ -72,6 +72,7 @@ The pull request team consists of a member of Umbraco HQ, [Sebastiaan](https://g
- [Anders Bjerner](https://github.com/abjerner)
- [Dave Woestenborghs](https://github.com/dawoe)
- [Emma Burstow](https://github.com/emmaburstow)
- [Kyle Weems](https://github.com/cssquirrel)
- [Poornima Nayar](https://github.com/poornimanayar)
These wonderful volunteers will provide you with a first reply to your PR, review and test out your changes and might ask more questions. After that they'll let Umbraco HQ know if everything seems okay.
@@ -81,7 +82,8 @@ These wonderful volunteers will provide you with a first reply to your PR, revie
You can get in touch with [the PR team](#the-pr-team) in multiple ways, we love open conversations and we are a friendly bunch. No question you have is stupid. Any questions you have usually helps out multiple people with the same question. Ask away:
- If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.com/forum/contributing-to-umbraco-cms/) forum, the team monitors that one closely
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.org/forum/contributing-to-umbraco-cms/) forum, the team monitors that one closely
- We're also [active in the Gitter chatroom](https://gitter.im/umbraco/Umbraco-CMS)
## Code of Conduct
@@ -19,19 +19,19 @@ When contributing code to Umbraco there's plenty of things you'll want to know,
* [What branch should I target for my contributions?](#what-branch-should-i-target-for-my-contributions)
* [Building Umbraco from source code](#building-umbraco-from-source-code)
* [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository)
## How Can I Contribute?
### Reporting Bugs
This section guides you through submitting a bug report for Umbraco CMS. Following these guidelines helps maintainers and the community understand your report 📝, reproduce the behavior 💻 💻, and find related reports 🔎.
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](https://github.com/umbraco/Umbraco-CMS/issues/new/choose), the information it asks for helps us resolve issues faster.
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](http://issues.umbraco.org/issues#newissue=61-30118), the information it asks for helps us resolve issues faster.
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
##### Before Submitting A Bug Report
* Most importantly, check **if you can reproduce the problem** in the [latest version of Umbraco](https://our.umbraco.com/download/). We might have already fixed your particular problem.
* Most importantly, check **if you can reproduce the problem** in the [latest version of Umbraco](https://our.umbraco.org/download/). We might have already fixed your particular problem.
* It also helps tremendously to check if the issue you're experiencing is present in **a clean install** of the Umbraco version you're currently using. Custom code can have side-effects that don't occur in a clean install.
* **Use the Google**. Whatever you're experiencing, Google it plus "Umbraco" - usually you can get some pretty good hints from the search results, including open issues and further troubleshooting hints.
* If you do find and existing issue has **and the issue is still open**, add a comment to the existing issue if you have additional information. If you have the same problem and no new info to add, just "star" the issue.
@@ -52,7 +52,7 @@ Provide more context by answering these questions:
Include details about your configuration and environment:
* **Which version of Umbraco are you using?**
* **Which version of Umbraco are you using?**
* **What is the environment you're using Umbraco in?** Is this a problem on your local machine or on a server. Tell us about your configuration: Windows version, IIS/IISExpress, database type, etc.
* **Which packages do you have installed?**
@@ -65,11 +65,13 @@ Most of the suggestions in the [reporting bugs](#reporting-bugs) section also co
Some additional hints that may be helpful:
* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of Umbraco which the suggestion is related to.
* **Explain why this enhancement would be useful to most Umbraco users** and isn't something that can or should be implemented as a [community package](https://our.umbraco.com/projects/).
* **Explain why this enhancement would be useful to most Umbraco users** and isn't something that can or should be implemented as a [community package](https://our.umbraco.org/projects/).
### Your First Code Contribution
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` and issues](https://issues.umbraco.org/issues?q=&project=U4&tagValue=upforgrabs&release=&issueType=&search=search) or on the [new issue tracker](https://github.com/umbraco/Umbraco-CMS/issues?q=is%3Aopen+is%3Aissue+label%3Acommunity%2Fup-for-grabs).
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` and issues](http://issues.umbraco.org/issues/U4?q=%28project%3A+%7BU4%7D+Difficulty%3A+%7BVery+Easy%7D+%23Easy+%23Unresolved+Priority%3A+Normal+%23Major+%23Show-stopper+State%3A+-%7BIn+Progress%7D+sort+by%3A+votes+Affected+versions%3A+-6.*+Affected+versions%3A+-4.*%29+OR+%28tag%3A+%7BUp+For+Grabs%7D+%23Unresolved+%29).
The issue list is sorted by total number of upvotes. While not perfect, number of upvotes is a reasonable proxy for impact a given change will have.
### Pull Requests
@@ -78,9 +80,9 @@ The most successful pull requests usually look a like this:
* Fill in the required template
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.com/documentation/Reference/) is generated
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.org/documentation/Reference/) is generated
Again, these are guidelines, not strict requirements.
Again, these are guidelines, not strict requirements.
## Making changes after the PR was opened
@@ -90,7 +92,7 @@ If you make the corrections we ask for in the same branch and push them to your
To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
## What should I know before I get started?
@@ -104,6 +106,7 @@ There's two big areas that you should know about:
You may need to run the following commands to set up gulp properly:
```
npm cache clean
npm install -g bower
npm install -g gulp
npm install -g gulp-cli
npm install
@@ -113,8 +116,8 @@ There's two big areas that you should know about:
To find the general areas of something you're looking to fix or improve, have a look at the following two parts of the API documentation.
* [The AngularJS based backoffice files](https://our.umbraco.com/apidocs/ui/#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
* [The rest](https://our.umbraco.com/apidocs/csharp/)
* [The AngularJS based backoffice files](https://our.umbraco.org/apidocs/ui/#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
* [The rest](https://our.umbraco.org/apidocs/csharp/)
### What branch should I target for my contributions?
@@ -124,15 +127,9 @@ We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-fl
### Building Umbraco from source code
In order to build the Umbraco source code locally, first make sure you have the following installed.
* Visual Studio 2017 v15.3+
* Node v10+ (Installed via `build.bat` script. If you already have it installed, make sure you're running at least v10)
* npm v6.4.1+ (Installed via `build.bat` script. If you already have it installed, make sure you're running at least v6.4.1)
The easiest way to get started is to run `build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`. See [this page](BUILD.md) for more details.
Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 (version 15.3 or higher, [the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile.
Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 ([the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile.
![Gulp build in Visual Studio](img/gulpbuild.png)
@@ -159,4 +156,4 @@ git rebase upstream/dev-v7
In this command we're syncing with the `dev-v7` branch, but you can of course choose another one if needed.
(More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated))
(More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated))
@@ -13,7 +13,7 @@ Were usually able to handle small PRs pretty quickly. A community volunteer w
Umbraco HQ will regularly mark newly created issues on the issue tracker with the `Up for grabs` tag. This means that the proposed changes are wanted in Umbraco but the HQ does not have the time to make them at this time. These issues are usually small enough to fit in the "Small PRs" category and we encourage anyone to pick them up and help out.
If you do start working on something, make sure to leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has.
If you do start working on something, make sure leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has.
## Large PRs
New features and large refactorings - can be recognized by seeing a large number of changes, plenty of new files, updates to package manager files (NuGets packages.config, NPMs packages.json, etc.).
@@ -30,4 +30,4 @@ If a larger pull request is encouraged by Umbraco HQ, the process will be simila
If it doesnt fit in CMS right now, we will likely encourage you to make it into a package instead. A package is a great way to check out popularity of a feature, learn how people use it, validate good usability and to fix bugs.
Eventually, a package could "graduate" to be included in the CMS.
Eventually, a package could "graduate" to be included in the CMS.
+11
View File
@@ -0,0 +1,11 @@
### Prerequisites
- [ ] I have written a descriptive pull-request title
- [ ] I have linked this PR to an issue on the tracker at http://issues.umbraco.org
### Description
<!-- A description of the changes proposed in the pull-request -->
<!-- Thanks for contributing to Umbraco CMS! -->
+51
View File
@@ -0,0 +1,51 @@
_You are browsing the Umbraco v8 branch._
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS) to go to the v7 branch._
_Ready to try out Version 8? [See the quick start guide](V8_GETTING_STARTED.md)._
Umbraco CMS
===========
The friendliest, most flexible and fastest growing ASP.NET CMS used by more than 443,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
[![ScreenShot](img/vimeo.png)](https://vimeo.com/172382998/)
## Umbraco CMS
Umbraco is a free open source Content Management System built on the ASP.NET platform. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
## Watch an introduction video
[![ScreenShot](https://shop.umbraco.com/images/whatisumbraco.png)](https://umbraco.tv/videos/umbraco-v7/content-editor/basics/introduction/cms-explanation/)
## Umbraco - The Friendly CMS
For the first time on the Microsoft platform, there is a free user and developer friendly CMS that makes it quick and easy to create websites - or a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, out of the box.
Umbraco is not only loved by developers, but is a content editors dream. Enjoy intuitive editing tools, media management, responsive views and approval workflows to send your content live.
Used by more than 443,000 active websites including Carlsberg, Segway, Amazon and Heinz and **The Official ASP.NET and IIS.NET website from Microsoft** ([https://asp.net](https://asp.net) / [https://iis.net](https://iis.net)), you can be sure that the technology is proven, stable and scales. Backed by the team at Umbraco HQ, and supported by a dedicated community of over 220,000 craftspeople globally, you can trust that Umbraco is a safe choice and is here to stay.
To view more examples, please visit [https://umbraco.com/case-studies-testimonials/](https://umbraco.com/case-studies-testimonials/)
## Why Open Source?
As an Open Source platform, Umbraco is more than just a CMS. We are transparent with our roadmap for future versions, our incremental sprint planning notes are publicly accessible and community contributions and packages are available for all to use.
## Trying out Umbraco CMS
[Umbraco Cloud](https://umbraco.com/cloud) is the easiest and fastest way to use Umbraco yet with full support for all your custom .NET code and intergrations. You're up and running in less than a minute and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14 day trial, no credit card needed.
If you want to DIY you can [download Umbraco](https://our.umbraco.com/download) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host yourself and handling deployments and upgrades is all down to you.
## Community
Our friendly community is available 24/7 at the community hub we call ["Our Umbraco"](https://our.umbraco.com). Our Umbraco feature forums for questions and answers, documentation, downloadable plugins for Umbraco and a rich collection of community resources.
## Contribute to Umbraco
Umbraco is contribution focused and community driven. If you want to contribute back to Umbraco please check out our [guide to contributing](CONTRIBUTING.md).
## Found a bug?
Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report refer to our [online guide for reporting issues](CONTRIBUTING_DETAILED.md#reporting-bugs).
To view existing issues, please visit [http://issues.umbraco.org](http://issues.umbraco.org).
@@ -11,7 +11,7 @@
* Open the `/src/umbraco.sln` Visual Studio solution
* Start the solution (easiest way is to use `ctrl + F5`)
* When the solution is first built this may take some time since it will restore all nuget and npm packages, build the .net solution and also build the angular solution
* When the solution is first built this may take some time since it will restore all nuget, npm and bower packages, build the .net solution and also build the angular solution
* When the website starts you'll see the Umbraco installer and just follow the prompts
* You're all set!
@@ -23,7 +23,7 @@ We recommend running the site with the Visual Studio since you'll be able to rem
### Making code changes
* _[The process for making code changes in v8 is the same as v7](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md)_
* _[The process for making code changes in v8 is the same as v7](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/docs/CONTRIBUTING.md)_
* Any .NET changes you make you just need to compile
* Any Angular/JS changes you make you will need to make sure you are running the Gulp build. Easiest way to do this is from within Visual Studio in the `Task Runner Explorer`. You can find this window by pressing `ctrl + q` and typing in `Task Runner Explorer`. In this window you'll see all Gulp tasks, double click on the `dev` task, this will compile the angular solution and start a file watcher, then any html/js changes you make are automatically built.
* When making js changes, you should have the chrome developer tools open to ensure that cache is disabled
@@ -33,5 +33,5 @@ We recommend running the site with the Visual Studio since you'll be able to rem
We are keeping track of [known issues and limitations here](http://issues.umbraco.org/issue/U4-11279). These line items will eventually be turned into actual tasks to be worked on. Feel free to help us keep this list updated if you find issues and even help fix some of these items. If there is a particular item you'd like to help fix please mention this on the task and we'll create a sub task for the item to continue discussion there.
There's [a list of tasks for v8 that haven't been completed](https://github.com/umbraco/Umbraco-CMS/labels/release%2F8.0.0). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done.
There's [a list of tasks for v8 that haven't been completed](http://issues.umbraco.org/issues/U4?q=Due+in+version%3A+8.0.0+%23Unresolved+). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

@@ -7,7 +7,7 @@
<span class="pull-right">
<a href="#top">Back to top</a>
</span>
<span>Copyright © 2016-present Umbraco<br>Generated by <strong>DocFX</strong></span>
<span>Copyright © 2016 Umbraco<br>Generated by <strong>DocFX</strong></span>
</div>
</div>
</footer>
@@ -8,7 +8,7 @@
<meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
<meta name="generator" content="docfx {{_docfxVersion}}">
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
<link rel="icon" type="image/png" href="https://our.umbraco.com/assets/images/app-icons/favicon.png">
<link rel="icon" type="image/png" href="https://our.umbraco.org/assets/images/app-icons/favicon.png">
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.css">
<link rel="stylesheet" href="{{_rel}}styles/docfx.css">
<link rel="stylesheet" href="{{_rel}}styles/main.css">
+1 -1
View File
@@ -63,7 +63,7 @@ a:focus {
}
.navbar-header .navbar-brand {
background: url(https://our.umbraco.com/assets/images/logo.svg) left center no-repeat;
background: url(https://our.umbraco.org/assets/images/logo.svg) left center no-repeat;
background-size: 40px auto;
width:50px;
}
+2 -2
View File
@@ -2,7 +2,7 @@
using System.Resources;
[assembly: AssemblyCompany("Umbraco")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2019")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -19,4 +19,4 @@ using System.Resources;
// these are FYI and changed automatically
[assembly: AssemblyFileVersion("8.0.0")]
[assembly: AssemblyInformationalVersion("8.0.0-alpha.58")]
[assembly: AssemblyInformationalVersion("8.0.0-alpha.44")]
+1 -1
View File
@@ -8,7 +8,7 @@ namespace Umbraco.Core
public static class Attempt
{
// note:
// cannot rely on overloads only to differentiate between with/without status
// cannot rely on overloads only to differenciate between with/without status
// in some cases it will always be ambiguous, so be explicit w/ 'WithStatus' methods
/// <summary>
+1 -1
View File
@@ -121,7 +121,7 @@ namespace Umbraco.Core
}
/// <summary>
/// Implicitly operator to check if the attempt was successful without having to access the 'success' property
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
+1 -1
View File
@@ -130,7 +130,7 @@ namespace Umbraco.Core
}
/// <summary>
/// Implicitly operator to check if the attempt was successful without having to access the 'success' property
/// Implicity operator to check if the attempt was successful without having to access the 'success' property
/// </summary>
/// <param name="a"></param>
/// <returns></returns>
@@ -3,7 +3,7 @@ using System.Linq;
using System.Text;
using System.Threading;
using System.Web;
using Umbraco.Core.Composing;
using Umbraco.Core.Components;
using Umbraco.Core.Events;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Membership;
@@ -11,38 +11,13 @@ using Umbraco.Core.Security;
using Umbraco.Core.Services;
using Umbraco.Core.Services.Implement;
namespace Umbraco.Core.Compose
namespace Umbraco.Core.Auditing
{
public sealed class AuditEventsComponent : IComponent
public sealed class AuditEventsComponent : UmbracoComponentBase, IUmbracoCoreComponent
{
private readonly IAuditService _auditService;
private readonly IUserService _userService;
private readonly IEntityService _entityService;
public AuditEventsComponent(IAuditService auditService, IUserService userService, IEntityService entityService)
{
_auditService = auditService;
_userService = userService;
_entityService = entityService;
}
public void Initialize()
{
UserService.SavedUserGroup += OnSavedUserGroupWithUsers;
UserService.SavedUser += OnSavedUser;
UserService.DeletedUser += OnDeletedUser;
UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned;
MemberService.Saved += OnSavedMember;
MemberService.Deleted += OnDeletedMember;
MemberService.AssignedRoles += OnAssignedRoles;
MemberService.RemovedRoles += OnRemovedRoles;
MemberService.Exported += OnMemberExported;
}
public void Terminate()
{ }
private IAuditService _auditService;
private IUserService _userService;
private IEntityService _entityService;
private IUser CurrentPerformingUser
{
@@ -72,6 +47,37 @@ namespace Umbraco.Core.Compose
}
}
public void Initialize(IAuditService auditService, IUserService userService, IEntityService entityService)
{
_auditService = auditService;
_userService = userService;
_entityService = entityService;
//BackOfficeUserManager.AccountLocked += ;
//BackOfficeUserManager.AccountUnlocked += ;
BackOfficeUserManager.ForgotPasswordRequested += OnForgotPasswordRequest;
BackOfficeUserManager.ForgotPasswordChangedSuccess += OnForgotPasswordChange;
BackOfficeUserManager.LoginFailed += OnLoginFailed;
//BackOfficeUserManager.LoginRequiresVerification += ;
BackOfficeUserManager.LoginSuccess += OnLoginSuccess;
BackOfficeUserManager.LogoutSuccess += OnLogoutSuccess;
BackOfficeUserManager.PasswordChanged += OnPasswordChanged;
BackOfficeUserManager.PasswordReset += OnPasswordReset;
//BackOfficeUserManager.ResetAccessFailedCount += ;
UserService.SavedUserGroup += OnSavedUserGroupWithUsers;
UserService.SavedUser += OnSavedUser;
UserService.DeletedUser += OnDeletedUser;
UserService.UserGroupPermissionsAssigned += UserGroupPermissionAssigned;
MemberService.Saved += OnSavedMember;
MemberService.Deleted += OnDeletedMember;
MemberService.AssignedRoles += OnAssignedRoles;
MemberService.RemovedRoles += OnRemovedRoles;
MemberService.Exported += OnMemberExported;
}
private string FormatEmail(IMember member)
{
return member == null ? string.Empty : member.Email.IsNullOrWhiteSpace() ? "" : $"<{member.Email}>";
@@ -249,6 +255,64 @@ namespace Umbraco.Core.Compose
"umbraco/user/delete", "delete user");
}
private void OnLoginSuccess(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs)
{
var performingUser = GetPerformingUser(identityArgs.PerformingUser);
WriteAudit(performingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/sign-in/login", "login success");
}
}
private void OnLogoutSuccess(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs)
{
var performingUser = GetPerformingUser(identityArgs.PerformingUser);
WriteAudit(performingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/sign-in/logout", "logout success");
}
}
private void OnPasswordReset(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/reset", "password reset");
}
}
private void OnPasswordChanged(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/change", "password change");
}
}
private void OnLoginFailed(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, 0, identityArgs.IpAddress, "umbraco/user/sign-in/failed", "login failed", affectedDetails: "");
}
}
private void OnForgotPasswordChange(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/forgot/change", "password forgot/change");
}
}
private void OnForgotPasswordRequest(object sender, EventArgs args)
{
if (args is IdentityAuditEventArgs identityArgs && identityArgs.PerformingUser >= 0)
{
WriteAudit(identityArgs.PerformingUser, identityArgs.AffectedUser, identityArgs.IpAddress, "umbraco/user/password/forgot/request", "password forgot/request");
}
}
private void WriteAudit(int performingId, int affectedId, string ipAddress, string eventType, string eventDetails, string affectedDetails = null)
{
var performingUser = _userService.GetUserById(performingId);
@@ -1,11 +1,13 @@
using System;
using System.ComponentModel;
using System.Threading;
using System.Web;
using Umbraco.Core.Security;
namespace Umbraco.Web.Security
namespace Umbraco.Core.Auditing
{
/// <summary>
/// This class is used by events raised from the BackofficeUserManager
/// This class is used by events raised from hthe BackofficeUserManager
/// </summary>
public class IdentityAuditEventArgs : EventArgs
{
@@ -30,7 +32,7 @@ namespace Umbraco.Web.Security
public int AffectedUser { get; private set; }
/// <summary>
/// If a user is performing an action on a different user, then this will be set. Otherwise it will be -1
/// If a user is perfoming an action on a different user, then this will be set. Otherwise it will be -1
/// </summary>
public int PerformingUser { get; private set; }
+10 -1
View File
@@ -18,7 +18,10 @@ namespace Umbraco.Core
// this only gets called when an assembly can't be resolved
AppDomain.CurrentDomain.AssemblyResolve += CurrentDomain_AssemblyResolve;
}
private static readonly Regex Log4NetAssemblyPattern = new Regex("log4net, Version=([\\d\\.]+?), Culture=neutral, PublicKeyToken=\\w+$", RegexOptions.Compiled);
private const string Log4NetReplacement = "log4net, Version=2.0.8.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a";
/// <summary>
/// This is used to do an assembly binding redirect via code - normally required due to signature changes in assemblies
/// </summary>
@@ -27,6 +30,12 @@ namespace Umbraco.Core
/// <returns></returns>
private static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
//log4net:
if (Log4NetAssemblyPattern.IsMatch(args.Name) && args.Name != Log4NetReplacement)
{
return Assembly.Load(Log4NetAssemblyPattern.Replace(args.Name, Log4NetReplacement));
}
//AutoMapper:
// ensure the assembly is indeed AutoMapper and that the PublicKeyToken is null before trying to Load again
// do NOT just replace this with 'return Assembly', as it will cause an infinite loop -> stackoverflow
+39
View File
@@ -0,0 +1,39 @@
namespace Umbraco.Core
{
public static class ByteArrayExtensions
{
private static readonly char[] BytesToHexStringLookup = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
public static string ToHexString(this byte[] bytes)
{
int i = 0, p = 0, bytesLength = bytes.Length;
var chars = new char[bytesLength * 2];
while (i < bytesLength)
{
var b = bytes[i++];
chars[p++] = BytesToHexStringLookup[b / 0x10];
chars[p++] = BytesToHexStringLookup[b % 0x10];
}
return new string(chars, 0, chars.Length);
}
public static string ToHexString(this byte[] bytes, char separator, int blockSize, int blockCount)
{
int p = 0, bytesLength = bytes.Length, count = 0, size = 0;
var chars = new char[bytesLength * 2 + blockCount];
for (var i = 0; i < bytesLength; i++)
{
var b = bytes[i++];
chars[p++] = BytesToHexStringLookup[b / 0x10];
chars[p++] = BytesToHexStringLookup[b % 0x10];
if (count == blockCount) continue;
if (++size < blockSize) continue;
chars[p++] = '/';
size = 0;
count++;
}
return new string(chars, 0, chars.Length);
}
}
}
-86
View File
@@ -1,86 +0,0 @@
using System;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Represents the application caches.
/// </summary>
public class AppCaches
{
/// <summary>
/// Initializes a new instance of the <see cref="AppCaches"/> for use in a web application.
/// </summary>
public AppCaches()
: this(HttpRuntime.Cache)
{ }
/// <summary>
/// Initializes a new instance of the <see cref="AppCaches"/> for use in a web application.
/// </summary>
public AppCaches(System.Web.Caching.Cache cache)
: this(
new WebCachingAppCache(cache),
new HttpRequestAppCache(),
new IsolatedCaches(t => new ObjectCacheAppCache()))
{ }
/// <summary>
/// Initializes a new instance of the <see cref="AppCaches"/> with cache providers.
/// </summary>
public AppCaches(
IAppPolicyCache runtimeCache,
IAppCache requestCache,
IsolatedCaches isolatedCaches)
{
RuntimeCache = runtimeCache ?? throw new ArgumentNullException(nameof(runtimeCache));
RequestCache = requestCache ?? throw new ArgumentNullException(nameof(requestCache));
IsolatedCaches = isolatedCaches ?? throw new ArgumentNullException(nameof(isolatedCaches));
}
/// <summary>
/// Gets the special disabled instance.
/// </summary>
/// <remarks>
/// <para>When used by repositories, all cache policies apply, but the underlying caches do not cache anything.</para>
/// <para>Used by tests.</para>
/// </remarks>
public static AppCaches Disabled { get; } = new AppCaches(NoAppCache.Instance, NoAppCache.Instance, new IsolatedCaches(_ => NoAppCache.Instance));
/// <summary>
/// Gets the special no-cache instance.
/// </summary>
/// <remarks>
/// <para>When used by repositories, all cache policies are bypassed.</para>
/// <para>Used by repositories that do no cache.</para>
/// </remarks>
public static AppCaches NoCache { get; } = new AppCaches(NoAppCache.Instance, NoAppCache.Instance, new IsolatedCaches(_ => NoAppCache.Instance));
/// <summary>
/// Gets the per-request cache.
/// </summary>
/// <remarks>
/// <para>The per-request caches works on top of the current HttpContext items.</para>
/// <para>Outside a web environment, the behavior of that cache is unspecified.</para>
/// </remarks>
public IAppCache RequestCache { get; }
/// <summary>
/// Gets the runtime cache.
/// </summary>
/// <remarks>
/// <para>The runtime cache is the main application cache.</para>
/// </remarks>
public IAppPolicyCache RuntimeCache { get; }
/// <summary>
/// Gets the isolated caches.
/// </summary>
/// <remarks>
/// <para>Isolated caches are used by e.g. repositories, to ensure that each cached entity
/// type has its own cache, so that lookups are fast and the repository does not need to
/// search through all keys on a global scale.</para>
/// </remarks>
public IsolatedCaches IsolatedCaches { get; }
}
}
@@ -1,74 +0,0 @@
using System;
using System.Collections.Concurrent;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Provides a base class for implementing a dictionary of <see cref="IAppPolicyCache"/>.
/// </summary>
/// <typeparam name="TKey">The type of the dictionary key.</typeparam>
public abstract class AppPolicedCacheDictionary<TKey>
{
private readonly ConcurrentDictionary<TKey, IAppPolicyCache> _caches = new ConcurrentDictionary<TKey, IAppPolicyCache>();
/// <summary>
/// Initializes a new instance of the <see cref="AppPolicedCacheDictionary{TKey}"/> class.
/// </summary>
/// <param name="cacheFactory"></param>
protected AppPolicedCacheDictionary(Func<TKey, IAppPolicyCache> cacheFactory)
{
CacheFactory = cacheFactory;
}
/// <summary>
/// Gets the internal cache factory, for tests only!
/// </summary>
internal readonly Func<TKey, IAppPolicyCache> CacheFactory;
/// <summary>
/// Gets or creates a cache.
/// </summary>
public IAppPolicyCache GetOrCreate(TKey key)
=> _caches.GetOrAdd(key, k => CacheFactory(k));
/// <summary>
/// Tries to get a cache.
/// </summary>
public Attempt<IAppPolicyCache> Get(TKey key)
=> _caches.TryGetValue(key, out var cache) ? Attempt.Succeed(cache) : Attempt.Fail<IAppPolicyCache>();
/// <summary>
/// Removes a cache.
/// </summary>
public void Remove(TKey key)
{
_caches.TryRemove(key, out _);
}
/// <summary>
/// Removes all caches.
/// </summary>
public void RemoveAll()
{
_caches.Clear();
}
/// <summary>
/// Clears a cache.
/// </summary>
public void ClearCache(TKey key)
{
if (_caches.TryGetValue(key, out var cache))
cache.Clear();
}
/// <summary>
/// Clears all caches.
/// </summary>
public void ClearAllCaches()
{
foreach (var cache in _caches.Values)
cache.Clear();
}
}
}
+99
View File
@@ -0,0 +1,99 @@
using System;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Class that is exposed by the ApplicationContext for application wide caching purposes
/// </summary>
public class CacheHelper
{
public static CacheHelper NoCache { get; } = new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
/// <summary>
/// Creates a cache helper with disabled caches
/// </summary>
/// <returns></returns>
/// <remarks>
/// Good for unit testing
/// </remarks>
public static CacheHelper CreateDisabledCacheHelper()
{
// do *not* return NoCache
// NoCache is a special instance that is detected by RepositoryBase and disables all cache policies
// CreateDisabledCacheHelper is used in tests to use no cache, *but* keep all cache policies
return new CacheHelper(NullCacheProvider.Instance, NullCacheProvider.Instance, NullCacheProvider.Instance, new IsolatedRuntimeCache(_ => NullCacheProvider.Instance));
}
/// <summary>
/// Initializes a new instance for use in the web
/// </summary>
public CacheHelper()
: this(
new HttpRuntimeCacheProvider(HttpRuntime.Cache),
new StaticCacheProvider(),
new HttpRequestCacheProvider(),
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
{
}
/// <summary>
/// Initializes a new instance for use in the web
/// </summary>
/// <param name="cache"></param>
public CacheHelper(System.Web.Caching.Cache cache)
: this(
new HttpRuntimeCacheProvider(cache),
new StaticCacheProvider(),
new HttpRequestCacheProvider(),
new IsolatedRuntimeCache(t => new ObjectCacheRuntimeCacheProvider()))
{
}
/// <summary>
/// Initializes a new instance based on the provided providers
/// </summary>
/// <param name="httpCacheProvider"></param>
/// <param name="staticCacheProvider"></param>
/// <param name="requestCacheProvider"></param>
/// <param name="isolatedCacheManager"></param>
public CacheHelper(
IRuntimeCacheProvider httpCacheProvider,
ICacheProvider staticCacheProvider,
ICacheProvider requestCacheProvider,
IsolatedRuntimeCache isolatedCacheManager)
{
if (httpCacheProvider == null) throw new ArgumentNullException("httpCacheProvider");
if (staticCacheProvider == null) throw new ArgumentNullException("staticCacheProvider");
if (requestCacheProvider == null) throw new ArgumentNullException("requestCacheProvider");
if (isolatedCacheManager == null) throw new ArgumentNullException("isolatedCacheManager");
RuntimeCache = httpCacheProvider;
StaticCache = staticCacheProvider;
RequestCache = requestCacheProvider;
IsolatedRuntimeCache = isolatedCacheManager;
}
/// <summary>
/// Returns the current Request cache
/// </summary>
public ICacheProvider RequestCache { get; internal set; }
/// <summary>
/// Returns the current Runtime cache
/// </summary>
public ICacheProvider StaticCache { get; internal set; }
/// <summary>
/// Returns the current Runtime cache
/// </summary>
public IRuntimeCacheProvider RuntimeCache { get; internal set; }
/// <summary>
/// Returns the current Isolated Runtime cache manager
/// </summary>
public IsolatedRuntimeCache IsolatedRuntimeCache { get; internal set; }
}
}
+2 -2
View File
@@ -5,10 +5,10 @@
/// </summary>
public static class CacheKeys
{
public const string ApplicationTreeCacheKey = "ApplicationTreeCache"; // used by ApplicationTreeService
public const string ApplicationsCacheKey = "ApplicationCache"; // used by SectionService
// TODO: this one can probably be removed
public const string TemplateFrontEndCacheKey = "template";
public const string TemplateFrontEndCacheKey = "template"; // fixme usage?
public const string MacroContentCacheKey = "macroContent_"; // used in MacroRenderers
}
@@ -8,9 +8,9 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Extensions for strongly typed access
/// </summary>
public static class AppCacheExtensions
public static class CacheProviderExtensions
{
public static T GetCacheItem<T>(this IAppPolicyCache provider,
public static T GetCacheItem<T>(this IRuntimeCacheProvider provider,
string cacheKey,
Func<T> getCacheItem,
TimeSpan? timeout,
@@ -19,11 +19,11 @@ namespace Umbraco.Core.Cache
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null)
{
var result = provider.Get(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
var result = provider.GetCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
return result == null ? default(T) : result.TryConvertTo<T>().Result;
}
public static void InsertCacheItem<T>(this IAppPolicyCache provider,
public static void InsertCacheItem<T>(this IRuntimeCacheProvider provider,
string cacheKey,
Func<T> getCacheItem,
TimeSpan? timeout = null,
@@ -32,24 +32,24 @@ namespace Umbraco.Core.Cache
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null)
{
provider.Insert(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
provider.InsertCacheItem(cacheKey, () => getCacheItem(), timeout, isSliding, priority, removedCallback, dependentFiles);
}
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this IAppCache provider, string keyStartsWith)
public static IEnumerable<T> GetCacheItemsByKeySearch<T>(this ICacheProvider provider, string keyStartsWith)
{
var result = provider.SearchByKey(keyStartsWith);
var result = provider.GetCacheItemsByKeySearch(keyStartsWith);
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this IAppCache provider, string regexString)
public static IEnumerable<T> GetCacheItemsByKeyExpression<T>(this ICacheProvider provider, string regexString)
{
var result = provider.SearchByRegex(regexString);
var result = provider.GetCacheItemsByKeyExpression(regexString);
return result.Select(x => x.TryConvertTo<T>().Result);
}
public static T GetCacheItem<T>(this IAppCache provider, string cacheKey)
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey)
{
var result = provider.Get(cacheKey);
var result = provider.GetCacheItem(cacheKey);
if (result == null)
{
return default(T);
@@ -57,9 +57,9 @@ namespace Umbraco.Core.Cache
return result.TryConvertTo<T>().Result;
}
public static T GetCacheItem<T>(this IAppCache provider, string cacheKey, Func<T> getCacheItem)
public static T GetCacheItem<T>(this ICacheProvider provider, string cacheKey, Func<T> getCacheItem)
{
var result = provider.Get(cacheKey, () => getCacheItem());
var result = provider.GetCacheItem(cacheKey, () => getCacheItem());
if (result == null)
{
return default(T);
+5 -5
View File
@@ -16,10 +16,10 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Initializes a new instance of the <see cref="CacheRefresherBase{TInstanceType}"/>.
/// </summary>
/// <param name="appCaches">A cache helper.</param>
protected CacheRefresherBase(AppCaches appCaches)
/// <param name="cacheHelper">A cache helper.</param>
protected CacheRefresherBase(CacheHelper cacheHelper)
{
AppCaches = appCaches;
CacheHelper = cacheHelper;
}
/// <summary>
@@ -93,7 +93,7 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Gets the cache helper.
/// </summary>
protected AppCaches AppCaches { get; }
protected CacheHelper CacheHelper { get; }
/// <summary>
/// Clears the cache for all repository entities of a specified type.
@@ -102,7 +102,7 @@ namespace Umbraco.Core.Cache
protected void ClearAllIsolatedCacheByEntityType<TEntity>()
where TEntity : class, IEntity
{
AppCaches.IsolatedCaches.ClearCache<TEntity>();
CacheHelper.IsolatedRuntimeCache.ClearCache<TEntity>();
}
/// <summary>
@@ -1,9 +1,15 @@
using Umbraco.Core.Composing;
using System.Collections.Generic;
using LightInject;
using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
public class CacheRefresherCollectionBuilder : LazyCollectionBuilderBase<CacheRefresherCollectionBuilder, CacheRefresherCollection, ICacheRefresher>
{
public CacheRefresherCollectionBuilder(IServiceContainer container)
: base(container)
{ }
protected override CacheRefresherCollectionBuilder This => this;
}
}
-157
View File
@@ -1,157 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements <see cref="IAppPolicyCache"/> by wrapping an inner other <see cref="IAppPolicyCache"/>
/// instance, and ensuring that all inserts and returns are deep cloned copies of the cache item,
/// when the item is deep-cloneable.
/// </summary>
internal class DeepCloneAppCache : IAppPolicyCache
{
/// <summary>
/// Initializes a new instance of the <see cref="DeepCloneAppCache"/> class.
/// </summary>
public DeepCloneAppCache(IAppPolicyCache innerCache)
{
var type = typeof (DeepCloneAppCache);
if (innerCache.GetType() == type)
throw new InvalidOperationException($"A {type} cannot wrap another instance of itself.");
InnerCache = innerCache;
}
/// <summary>
/// Gets the inner cache.
/// </summary>
public IAppPolicyCache InnerCache { get; }
/// <inheritdoc />
public object Get(string key)
{
var item = InnerCache.Get(key);
return CheckCloneableAndTracksChanges(item);
}
/// <inheritdoc />
public object Get(string key, Func<object> factory)
{
var cached = InnerCache.Get(key, () =>
{
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
// do not store null values (backward compat), clone / reset to go into the cache
return value == null ? null : CheckCloneableAndTracksChanges(value);
});
return CheckCloneableAndTracksChanges(cached);
}
/// <inheritdoc />
public IEnumerable<object> SearchByKey(string keyStartsWith)
{
return InnerCache.SearchByKey(keyStartsWith)
.Select(CheckCloneableAndTracksChanges);
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
return InnerCache.SearchByRegex(regex)
.Select(CheckCloneableAndTracksChanges);
}
/// <inheritdoc />
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
var cached = InnerCache.Get(key, () =>
{
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
// do not store null values (backward compat), clone / reset to go into the cache
return value == null ? null : CheckCloneableAndTracksChanges(value);
// clone / reset to go into the cache
}, timeout, isSliding, priority, removedCallback, dependentFiles);
// clone / reset to go into the cache
return CheckCloneableAndTracksChanges(cached);
}
/// <inheritdoc />
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
InnerCache.Insert(key, () =>
{
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
// do not store null values (backward compat), clone / reset to go into the cache
return value == null ? null : CheckCloneableAndTracksChanges(value);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
}
/// <inheritdoc />
public void Clear()
{
InnerCache.Clear();
}
/// <inheritdoc />
public void Clear(string key)
{
InnerCache.Clear(key);
}
/// <inheritdoc />
public void ClearOfType(string typeName)
{
InnerCache.ClearOfType(typeName);
}
/// <inheritdoc />
public void ClearOfType<T>()
{
InnerCache.ClearOfType<T>();
}
/// <inheritdoc />
public void ClearOfType<T>(Func<string, T, bool> predicate)
{
InnerCache.ClearOfType<T>(predicate);
}
/// <inheritdoc />
public void ClearByKey(string keyStartsWith)
{
InnerCache.ClearByKey(keyStartsWith);
}
/// <inheritdoc />
public void ClearByRegex(string regex)
{
InnerCache.ClearByRegex(regex);
}
private static object CheckCloneableAndTracksChanges(object input)
{
if (input is IDeepCloneable cloneable)
{
input = cloneable.DeepClone();
}
// reset dirty initial properties
if (input is IRememberBeingDirty tracksChanges)
{
tracksChanges.ResetDirtyProperties(false);
input = tracksChanges;
}
return input;
}
}
}
@@ -0,0 +1,155 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
using Umbraco.Core.Models;
using Umbraco.Core.Models.Entities;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Interface describing this cache provider as a wrapper for another
/// </summary>
internal interface IRuntimeCacheProviderWrapper
{
IRuntimeCacheProvider InnerProvider { get; }
}
/// <summary>
/// A wrapper for any IRuntimeCacheProvider that ensures that all inserts and returns
/// are a deep cloned copy of the item when the item is IDeepCloneable and that tracks changes are
/// reset if the object is TracksChangesEntityBase
/// </summary>
internal class DeepCloneRuntimeCacheProvider : IRuntimeCacheProvider, IRuntimeCacheProviderWrapper
{
public IRuntimeCacheProvider InnerProvider { get; }
public DeepCloneRuntimeCacheProvider(IRuntimeCacheProvider innerProvider)
{
var type = typeof (DeepCloneRuntimeCacheProvider);
if (innerProvider.GetType() == type)
throw new InvalidOperationException($"A {type} cannot wrap another instance of {type}.");
InnerProvider = innerProvider;
}
#region Clear - doesn't require any changes
public void ClearAllCache()
{
InnerProvider.ClearAllCache();
}
public void ClearCacheItem(string key)
{
InnerProvider.ClearCacheItem(key);
}
public void ClearCacheObjectTypes(string typeName)
{
InnerProvider.ClearCacheObjectTypes(typeName);
}
public void ClearCacheObjectTypes<T>()
{
InnerProvider.ClearCacheObjectTypes<T>();
}
public void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
InnerProvider.ClearCacheObjectTypes<T>(predicate);
}
public void ClearCacheByKeySearch(string keyStartsWith)
{
InnerProvider.ClearCacheByKeySearch(keyStartsWith);
}
public void ClearCacheByKeyExpression(string regexString)
{
InnerProvider.ClearCacheByKeyExpression(regexString);
}
#endregion
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return InnerProvider.GetCacheItemsByKeySearch(keyStartsWith)
.Select(CheckCloneableAndTracksChanges);
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return InnerProvider.GetCacheItemsByKeyExpression(regexString)
.Select(CheckCloneableAndTracksChanges);
}
public object GetCacheItem(string cacheKey)
{
var item = InnerProvider.GetCacheItem(cacheKey);
return CheckCloneableAndTracksChanges(item);
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
var cached = InnerProvider.GetCacheItem(cacheKey, () =>
{
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return null; // do not store null values (backward compat)
return CheckCloneableAndTracksChanges(value);
});
return CheckCloneableAndTracksChanges(cached);
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
var cached = InnerProvider.GetCacheItem(cacheKey, () =>
{
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return null; // do not store null values (backward compat)
// clone / reset to go into the cache
return CheckCloneableAndTracksChanges(value);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
// clone / reset to go into the cache
return CheckCloneableAndTracksChanges(cached);
}
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
InnerProvider.InsertCacheItem(cacheKey, () =>
{
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
var value = result.Value; // force evaluation now - this may throw if cacheItem throws, and then nothing goes into cache
if (value == null) return null; // do not store null values (backward compat)
// clone / reset to go into the cache
return CheckCloneableAndTracksChanges(value);
}, timeout, isSliding, priority, removedCallback, dependentFiles);
}
private static object CheckCloneableAndTracksChanges(object input)
{
var cloneable = input as IDeepCloneable;
if (cloneable != null)
{
input = cloneable.DeepClone();
}
// reset dirty initial properties (U4-1946)
var tracksChanges = input as IRememberBeingDirty;
if (tracksChanges != null)
{
tracksChanges.ResetDirtyProperties(false);
input = tracksChanges;
}
return input;
}
}
}
@@ -23,7 +23,7 @@ namespace Umbraco.Core.Cache
private static readonly TEntity[] EmptyEntities = new TEntity[0]; // const
private readonly RepositoryCachePolicyOptions _options;
public DefaultRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
public DefaultRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
: base(cache, scopeAccessor)
{
_options = options ?? throw new ArgumentNullException(nameof(options));
@@ -42,7 +42,7 @@ namespace Umbraco.Core.Cache
protected virtual void InsertEntity(string cacheKey, TEntity entity)
{
Cache.Insert(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(cacheKey, () => entity, TimeSpan.FromMinutes(5), true);
}
protected virtual void InsertEntities(TId[] ids, TEntity[] entities)
@@ -52,7 +52,7 @@ namespace Umbraco.Core.Cache
// getting all of them, and finding nothing.
// if we can cache a zero count, cache an empty array,
// for as long as the cache is not cleared (no expiration)
Cache.Insert(GetEntityTypeCacheKey(), () => EmptyEntities);
Cache.InsertCacheItem(GetEntityTypeCacheKey(), () => EmptyEntities);
}
else
{
@@ -60,7 +60,7 @@ namespace Umbraco.Core.Cache
foreach (var entity in entities)
{
var capture = entity;
Cache.Insert(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => capture, TimeSpan.FromMinutes(5), true);
}
}
}
@@ -77,21 +77,21 @@ namespace Umbraco.Core.Cache
// just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
Cache.Insert(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
}
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
catch
{
// if an exception is thrown we need to remove the entry from cache,
// this is ONLY a work around because of the way
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
Cache.Clear(GetEntityCacheKey(entity.Id));
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
throw;
}
@@ -109,21 +109,21 @@ namespace Umbraco.Core.Cache
// just to be safe, we cannot cache an item without an identity
if (entity.HasIdentity)
{
Cache.Insert(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(GetEntityCacheKey(entity.Id), () => entity, TimeSpan.FromMinutes(5), true);
}
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
catch
{
// if an exception is thrown we need to remove the entry from cache,
// this is ONLY a work around because of the way
// that we cache entities: http://issues.umbraco.org/issue/U4-4259
Cache.Clear(GetEntityCacheKey(entity.Id));
Cache.ClearCacheItem(GetEntityCacheKey(entity.Id));
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
throw;
}
@@ -142,9 +142,9 @@ namespace Umbraco.Core.Cache
{
// whatever happens, clear the cache
var cacheKey = GetEntityCacheKey(entity.Id);
Cache.Clear(cacheKey);
Cache.ClearCacheItem(cacheKey);
// if there's a GetAllCacheAllowZeroCount cache, ensure it is cleared
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
}
@@ -238,7 +238,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void ClearAll()
{
Cache.ClearByKey(GetEntityTypeCacheKey());
Cache.ClearCacheByKeySearch(GetEntityTypeCacheKey());
}
}
}
@@ -1,96 +0,0 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Text.RegularExpressions;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements <see cref="IAppCache"/> on top of a concurrent dictionary.
/// </summary>
public class DictionaryAppCache : IAppCache
{
/// <summary>
/// Gets the internal items dictionary, for tests only!
/// </summary>
internal readonly ConcurrentDictionary<string, object> Items = new ConcurrentDictionary<string, object>();
/// <inheritdoc />
public virtual object Get(string key)
{
return Items.TryGetValue(key, out var value) ? value : null;
}
/// <inheritdoc />
public virtual object Get(string key, Func<object> factory)
{
return Items.GetOrAdd(key, _ => factory());
}
/// <inheritdoc />
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
{
var items = new List<object>();
foreach (var (key, value) in Items)
if (key.InvariantStartsWith(keyStartsWith))
items.Add(value);
return items;
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
var items = new List<object>();
foreach (var (key, value) in Items)
if (compiled.IsMatch(key))
items.Add(value);
return items;
}
/// <inheritdoc />
public virtual void Clear()
{
Items.Clear();
}
/// <inheritdoc />
public virtual void Clear(string key)
{
Items.TryRemove(key, out _);
}
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
{
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName));
}
/// <inheritdoc />
public virtual void ClearOfType<T>()
{
var typeOfT = typeof(T);
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT);
}
/// <inheritdoc />
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
Items.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value));
}
/// <inheritdoc />
public virtual void ClearByKey(string keyStartsWith)
{
Items.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith));
}
/// <inheritdoc />
public virtual void ClearByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
Items.RemoveAll(kvp => compiled.IsMatch(kvp.Key));
}
}
}
@@ -7,130 +7,79 @@ using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements a fast <see cref="IAppCache"/> on top of a concurrent dictionary.
/// </summary>
internal class FastDictionaryAppCache : IAppCache
internal class DictionaryCacheProvider : ICacheProvider
{
/// <summary>
/// Gets the internal items dictionary, for tests only!
/// </summary>
internal readonly ConcurrentDictionary<string, Lazy<object>> Items = new ConcurrentDictionary<string, Lazy<object>>();
private readonly ConcurrentDictionary<string, Lazy<object>> _items
= new ConcurrentDictionary<string, Lazy<object>>();
/// <inheritdoc />
public object Get(string cacheKey)
// for tests
internal ConcurrentDictionary<string, Lazy<object>> Items => _items;
public void ClearAllCache()
{
Items.TryGetValue(cacheKey, out var result); // else null
return result == null ? null : FastDictionaryAppCacheBase.GetSafeLazyValue(result); // return exceptions as null
_items.Clear();
}
/// <inheritdoc />
public object Get(string cacheKey, Func<object> getCacheItem)
public void ClearCacheItem(string key)
{
var result = Items.GetOrAdd(cacheKey, k => FastDictionaryAppCacheBase.GetSafeLazy(getCacheItem));
var value = result.Value; // will not throw (safe lazy)
if (!(value is FastDictionaryAppCacheBase.ExceptionHolder eh))
return value;
// and... it's in the cache anyway - so contrary to other cache providers,
// which would trick with GetSafeLazyValue, we need to remove by ourselves,
// in order NOT to cache exceptions
Items.TryRemove(cacheKey, out result);
eh.Exception.Throw(); // throw once!
return null; // never reached
_items.TryRemove(key, out _);
}
/// <inheritdoc />
public IEnumerable<object> SearchByKey(string keyStartsWith)
{
return Items
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))
.Select(kvp => FastDictionaryAppCacheBase.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
return Items
.Where(kvp => compiled.IsMatch(kvp.Key))
.Select(kvp => FastDictionaryAppCacheBase.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
}
/// <inheritdoc />
public void Clear()
{
Items.Clear();
}
/// <inheritdoc />
public void Clear(string key)
{
Items.TryRemove(key, out _);
}
/// <inheritdoc />
public void ClearOfType(string typeName)
public void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
var isInterface = type.IsInterface;
foreach (var kvp in Items
foreach (var kvp in _items
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (type.IsInstanceOfType(value)) : (value.GetType() == type));
}))
Items.TryRemove(kvp.Key, out _);
_items.TryRemove(kvp.Key, out _);
}
/// <inheritdoc />
public void ClearOfType<T>()
public void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var kvp in Items
foreach (var kvp in _items
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
return value == null || (isInterface ? (value is T) : (value.GetType() == typeOfT));
}))
Items.TryRemove(kvp.Key, out _);
_items.TryRemove(kvp.Key, out _);
}
/// <inheritdoc />
public void ClearOfType<T>(Func<string, T, bool> predicate)
public void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
foreach (var kvp in Items
foreach (var kvp in _items
.Where(x =>
{
// entry.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// compare on exact type, don't use "is"
// get non-created as NonCreatedValue & exceptions as null
var value = FastDictionaryAppCacheBase.GetSafeLazyValue(x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue(x.Value, true);
if (value == null) return true;
// if T is an interface remove anything that implements that interface
@@ -139,24 +88,60 @@ namespace Umbraco.Core.Cache
// run predicate on the 'public key' part only, ie without prefix
&& predicate(x.Key, (T)value);
}))
Items.TryRemove(kvp.Key, out _);
_items.TryRemove(kvp.Key, out _);
}
/// <inheritdoc />
public void ClearByKey(string keyStartsWith)
public void ClearCacheByKeySearch(string keyStartsWith)
{
foreach (var ikvp in Items
foreach (var ikvp in _items
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith)))
Items.TryRemove(ikvp.Key, out _);
_items.TryRemove(ikvp.Key, out _);
}
/// <inheritdoc />
public void ClearByRegex(string regex)
public void ClearCacheByKeyExpression(string regexString)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
foreach (var ikvp in Items
.Where(kvp => compiled.IsMatch(kvp.Key)))
Items.TryRemove(ikvp.Key, out _);
foreach (var ikvp in _items
.Where(kvp => Regex.IsMatch(kvp.Key, regexString)))
_items.TryRemove(ikvp.Key, out _);
}
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return _items
.Where(kvp => kvp.Key.InvariantStartsWith(keyStartsWith))
.Select(kvp => DictionaryCacheProviderBase.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return _items
.Where(kvp => Regex.IsMatch(kvp.Key, regexString))
.Select(kvp => DictionaryCacheProviderBase.GetSafeLazyValue(kvp.Value))
.Where(x => x != null);
}
public object GetCacheItem(string cacheKey)
{
_items.TryGetValue(cacheKey, out var result); // else null
return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
var result = _items.GetOrAdd(cacheKey, k => DictionaryCacheProviderBase.GetSafeLazy(getCacheItem));
var value = result.Value; // will not throw (safe lazy)
if (!(value is DictionaryCacheProviderBase.ExceptionHolder eh))
return value;
// and... it's in the cache anyway - so contrary to other cache providers,
// which would trick with GetSafeLazyValue, we need to remove by ourselves,
// in order NOT to cache exceptions
_items.TryRemove(cacheKey, out result);
eh.Exception.Throw(); // throw once!
return null; // never reached
}
}
}
@@ -8,10 +8,7 @@ using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Provides a base class to fast, dictionary-based <see cref="IAppCache"/> implementations.
/// </summary>
internal abstract class FastDictionaryAppCacheBase : IAppCache
internal abstract class DictionaryCacheProviderBase : ICacheProvider
{
// prefix cache keys so we know which one are ours
protected const string CacheItemPrefix = "umbrtmche";
@@ -19,75 +16,82 @@ namespace Umbraco.Core.Cache
// an object that represent a value that has not been created yet
protected internal static readonly object ValueNotCreated = new object();
#region IAppCache
// manupulate the underlying cache entries
// these *must* be called from within the appropriate locks
// and use the full prefixed cache keys
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
protected abstract void RemoveEntry(string key);
protected abstract object GetEntry(string key);
/// <inheritdoc />
public virtual object Get(string key)
// read-write lock the underlying cache
//protected abstract IDisposable ReadLock { get; }
//protected abstract IDisposable WriteLock { get; }
protected abstract void EnterReadLock();
protected abstract void ExitReadLock();
protected abstract void EnterWriteLock();
protected abstract void ExitWriteLock();
protected string GetCacheKey(string key)
{
key = GetCacheKey(key);
Lazy<object> result;
try
{
EnterReadLock();
result = GetEntry(key) as Lazy<object>; // null if key not found
}
finally
{
ExitReadLock();
}
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
return string.Format("{0}-{1}", CacheItemPrefix, key);
}
/// <inheritdoc />
public abstract object Get(string key, Func<object> factory);
/// <inheritdoc />
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
{
var plen = CacheItemPrefix.Length + 1;
IEnumerable<DictionaryEntry> entries;
try
// try to generate the value and if it fails,
// wrap in an ExceptionHolder - would be much simpler
// to just use lazy.IsValueFaulted alas that field is
// internal
return new Lazy<object>(() =>
{
EnterReadLock();
entries = GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
finally
{
ExitReadLock();
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compat, don't store null values in the cache
try
{
return getCacheItem();
}
catch (Exception e)
{
return new ExceptionHolder(ExceptionDispatchInfo.Capture(e));
}
});
}
/// <inheritdoc />
public virtual IEnumerable<object> SearchByRegex(string regex)
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
{
const string prefix = CacheItemPrefix + "-";
var compiled = new Regex(regex, RegexOptions.Compiled);
var plen = prefix.Length;
IEnumerable<DictionaryEntry> entries;
// if onlyIfValueIsCreated, do not trigger value creation
// must return something, though, to differenciate from null values
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
// if execution has thrown then lazy.IsValueCreated is false
// and lazy.IsValueFaulted is true (but internal) so we use our
// own exception holder (see Lazy<T> source code) to return null
if (lazy.Value is ExceptionHolder) return null;
// we have a value and execution has not thrown so returning
// here does not throw - unless we're re-entering, take care of it
try
{
EnterReadLock();
entries = GetDictionaryEntries()
.Where(x => compiled.IsMatch(((string)x.Key).Substring(plen)))
.ToArray(); // evaluate while locked
return lazy.Value;
}
finally
catch (InvalidOperationException e)
{
ExitReadLock();
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compatible, don't store null values in the cache
}
/// <inheritdoc />
public virtual void Clear()
internal class ExceptionHolder
{
public ExceptionHolder(ExceptionDispatchInfo e)
{
Exception = e;
}
public ExceptionDispatchInfo Exception { get; }
}
#region Clear
public virtual void ClearAllCache()
{
try
{
@@ -102,8 +106,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void Clear(string key)
public virtual void ClearCacheItem(string key)
{
var cacheKey = GetCacheKey(key);
try
@@ -117,8 +120,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
public virtual void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
@@ -147,8 +149,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType<T>()
public virtual void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
@@ -177,8 +178,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
var isInterface = typeOfT.IsInterface;
@@ -210,8 +210,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearByKey(string keyStartsWith)
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
var plen = CacheItemPrefix.Length + 1;
try
@@ -228,16 +227,14 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearByRegex(string regex)
public virtual void ClearCacheByKeyExpression(string regexString)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
var plen = CacheItemPrefix.Length + 1;
try
{
EnterWriteLock();
foreach (var entry in GetDictionaryEntries()
.Where(x => compiled.IsMatch(((string)x.Key).Substring(plen)))
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray())
RemoveEntry((string) entry.Key);
}
@@ -249,81 +246,68 @@ namespace Umbraco.Core.Cache
#endregion
#region Dictionary
#region Get
// manipulate the underlying cache entries
// these *must* be called from within the appropriate locks
// and use the full prefixed cache keys
protected abstract IEnumerable<DictionaryEntry> GetDictionaryEntries();
protected abstract void RemoveEntry(string key);
protected abstract object GetEntry(string key);
// read-write lock the underlying cache
//protected abstract IDisposable ReadLock { get; }
//protected abstract IDisposable WriteLock { get; }
protected abstract void EnterReadLock();
protected abstract void ExitReadLock();
protected abstract void EnterWriteLock();
protected abstract void ExitWriteLock();
protected string GetCacheKey(string key)
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return $"{CacheItemPrefix}-{key}";
}
protected internal static Lazy<object> GetSafeLazy(Func<object> getCacheItem)
{
// try to generate the value and if it fails,
// wrap in an ExceptionHolder - would be much simpler
// to just use lazy.IsValueFaulted alas that field is
// internal
return new Lazy<object>(() =>
{
try
{
return getCacheItem();
}
catch (Exception e)
{
return new ExceptionHolder(ExceptionDispatchInfo.Capture(e));
}
});
}
protected internal static object GetSafeLazyValue(Lazy<object> lazy, bool onlyIfValueIsCreated = false)
{
// if onlyIfValueIsCreated, do not trigger value creation
// must return something, though, to differentiate from null values
if (onlyIfValueIsCreated && lazy.IsValueCreated == false) return ValueNotCreated;
// if execution has thrown then lazy.IsValueCreated is false
// and lazy.IsValueFaulted is true (but internal) so we use our
// own exception holder (see Lazy<T> source code) to return null
if (lazy.Value is ExceptionHolder) return null;
// we have a value and execution has not thrown so returning
// here does not throw - unless we're re-entering, take care of it
var plen = CacheItemPrefix.Length + 1;
IEnumerable<DictionaryEntry> entries;
try
{
return lazy.Value;
EnterReadLock();
entries = GetDictionaryEntries()
.Where(x => ((string)x.Key).Substring(plen).InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
catch (InvalidOperationException e)
finally
{
throw new InvalidOperationException("The method that computes a value for the cache has tried to read that value from the cache.", e);
ExitReadLock();
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compat, don't store null values in the cache
}
internal class ExceptionHolder
public virtual IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
public ExceptionHolder(ExceptionDispatchInfo e)
const string prefix = CacheItemPrefix + "-";
var plen = prefix.Length;
IEnumerable<DictionaryEntry> entries;
try
{
Exception = e;
EnterReadLock();
entries = GetDictionaryEntries()
.Where(x => Regex.IsMatch(((string)x.Key).Substring(plen), regexString))
.ToArray(); // evaluate while locked
}
public ExceptionDispatchInfo Exception { get; }
finally
{
ExitReadLock();
}
return entries
.Select(x => GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null); // backward compat, don't store null values in the cache
}
public virtual object GetCacheItem(string cacheKey)
{
cacheKey = GetCacheKey(cacheKey);
Lazy<object> result;
try
{
EnterReadLock();
result = GetEntry(cacheKey) as Lazy<object>; // null if key not found
}
finally
{
ExitReadLock();
}
return result == null ? null : GetSafeLazyValue(result); // return exceptions as null
}
public abstract object GetCacheItem(string cacheKey, Func<object> getCacheItem);
#endregion
}
}
@@ -24,7 +24,7 @@ namespace Umbraco.Core.Cache
private readonly Func<TEntity, TId> _entityGetId;
private readonly bool _expires;
public FullDataSetRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, Func<TEntity, TId> entityGetId, bool expires)
public FullDataSetRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, Func<TEntity, TId> entityGetId, bool expires)
: base(cache, scopeAccessor)
{
_entityGetId = entityGetId;
@@ -55,11 +55,11 @@ namespace Umbraco.Core.Cache
if (_expires)
{
Cache.Insert(key, () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities), TimeSpan.FromMinutes(5), true);
}
else
{
Cache.Insert(key, () => new DeepCloneableList<TEntity>(entities));
Cache.InsertCacheItem(key, () => new DeepCloneableList<TEntity>(entities));
}
}
@@ -171,7 +171,7 @@ namespace Umbraco.Core.Cache
/// <inheritdoc />
public override void ClearAll()
{
Cache.Clear(GetEntityTypeCacheKey());
Cache.ClearCacheItem(GetEntityTypeCacheKey());
}
}
}
@@ -1,144 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements a fast <see cref="IAppCache"/> on top of HttpContext.Items.
/// </summary>
/// <remarks>
/// <para>If no current HttpContext items can be found (no current HttpContext,
/// or no Items...) then this cache acts as a pass-through and does not cache
/// anything.</para>
/// </remarks>
internal class HttpRequestAppCache : FastDictionaryAppCacheBase
{
private readonly HttpContextBase _context;
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class with a context, for unit tests!
/// </summary>
public HttpRequestAppCache(HttpContextBase context)
{
_context = context;
}
/// <summary>
/// Initializes a new instance of the <see cref="HttpRequestAppCache"/> class.
/// </summary>
/// <remarks>
/// <para>Will use HttpContext.Current.</para>
/// TODO: https://github.com/umbraco/Umbraco-CMS/issues/4239 - use IHttpContextAccessor NOT HttpContext.Current
/// </remarks>
public HttpRequestAppCache()
{ }
private IDictionary ContextItems => _context?.Items ?? HttpContext.Current?.Items;
private bool HasContextItems => _context?.Items != null || HttpContext.Current != null;
/// <inheritdoc />
public override object Get(string key, Func<object> factory)
{
//no place to cache so just return the callback result
if (HasContextItems == false) return factory();
key = GetCacheKey(key);
Lazy<object> result;
try
{
EnterWriteLock();
result = ContextItems[key] as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
// more than one thread to reach the return statement below and throw - accepted.
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = GetSafeLazy(factory);
ContextItems[key] = result;
}
}
finally
{
ExitWriteLock();
}
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
// exceptions (but try again and again) and silently eat them - however at
// some point we have to report them - so need to re-throw here
// this does not throw anymore
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
if (value is ExceptionHolder eh) eh.Exception.Throw(); // throw once!
return value;
}
#region Entries
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
if (HasContextItems == false) return Enumerable.Empty<DictionaryEntry>();
return ContextItems.Cast<DictionaryEntry>()
.Where(x => x.Key is string s && s.StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
if (HasContextItems == false) return;
ContextItems.Remove(key);
}
protected override object GetEntry(string key)
{
return HasContextItems ? ContextItems[key] : null;
}
#endregion
#region Lock
private const string ContextItemsLockKey = "Umbraco.Core.Cache.HttpRequestCache::LockEntered";
protected override void EnterReadLock() => EnterWriteLock();
protected override void EnterWriteLock()
{
if (HasContextItems)
{
// note: cannot keep 'entered' as a class variable here,
// since there is one per request - so storing it within
// ContextItems - which is locked, so this should be safe
var entered = false;
Monitor.Enter(ContextItems.SyncRoot, ref entered);
ContextItems[ContextItemsLockKey] = entered;
}
}
protected override void ExitReadLock() => ExitWriteLock();
protected override void ExitWriteLock()
{
var entered = (bool?) ContextItems[ContextItemsLockKey] ?? false;
if (entered)
Monitor.Exit(ContextItems.SyncRoot);
ContextItems.Remove(ContextItemsLockKey);
}
#endregion
}
}
@@ -0,0 +1,161 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web;
namespace Umbraco.Core.Cache
{
/// <summary>
/// A cache provider that caches items in the HttpContext.Items
/// </summary>
/// <remarks>
/// If the Items collection is null, then this provider has no effect
/// </remarks>
internal class HttpRequestCacheProvider : DictionaryCacheProviderBase
{
// context provider
// the idea is that there is only one, application-wide HttpRequestCacheProvider instance,
// that is initialized with a method that returns the "current" context.
// NOTE
// but then it is initialized with () => new HttpContextWrapper(HttpContent.Current)
// which is higly inefficient because it creates a new wrapper each time we refer to _context()
// so replace it with _context1 and _context2 below + a way to get context.Items.
//private readonly Func<HttpContextBase> _context;
// NOTE
// and then in almost 100% cases _context2 will be () => HttpContext.Current
// so why not bring that logic in here and fallback on to HttpContext.Current when
// _context1 is null?
//private readonly HttpContextBase _context1;
//private readonly Func<HttpContext> _context2;
private readonly HttpContextBase _context;
private IDictionary ContextItems
{
//get { return _context1 != null ? _context1.Items : _context2().Items; }
get { return _context != null ? _context.Items : HttpContext.Current.Items; }
}
private bool HasContextItems
{
get { return (_context != null && _context.Items != null) || HttpContext.Current != null; }
}
// for unit tests
public HttpRequestCacheProvider(HttpContextBase context)
{
_context = context;
}
// main constructor
// will use HttpContext.Current
public HttpRequestCacheProvider(/*Func<HttpContext> context*/)
{
//_context2 = context;
}
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
if (HasContextItems == false) return Enumerable.Empty<DictionaryEntry>();
return ContextItems.Cast<DictionaryEntry>()
.Where(x => x.Key is string && ((string)x.Key).StartsWith(prefix));
}
protected override void RemoveEntry(string key)
{
if (HasContextItems == false) return;
ContextItems.Remove(key);
}
protected override object GetEntry(string key)
{
return HasContextItems ? ContextItems[key] : null;
}
#region Lock
private bool _entered;
protected override void EnterReadLock() => EnterWriteLock();
protected override void EnterWriteLock()
{
if (HasContextItems)
{
System.Threading.Monitor.Enter(ContextItems.SyncRoot, ref _entered);
}
}
protected override void ExitReadLock() => ExitWriteLock();
protected override void ExitWriteLock()
{
if (_entered)
{
_entered = false;
System.Threading.Monitor.Exit(ContextItems.SyncRoot);
}
}
#endregion
#region Get
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
//no place to cache so just return the callback result
if (HasContextItems == false) return getCacheItem();
cacheKey = GetCacheKey(cacheKey);
Lazy<object> result;
try
{
EnterWriteLock();
result = ContextItems[cacheKey] as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
// more than one thread to reach the return statement below and throw - accepted.
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = GetSafeLazy(getCacheItem);
ContextItems[cacheKey] = result;
}
}
finally
{
ExitWriteLock();
}
// using GetSafeLazy and GetSafeLazyValue ensures that we don't cache
// exceptions (but try again and again) and silently eat them - however at
// some point we have to report them - so need to re-throw here
// this does not throw anymore
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
if (value is ExceptionHolder eh) eh.Exception.Throw(); // throw once!
return value;
}
#endregion
#region Insert
#endregion
private class NoopLocker : DisposableObjectSlim
{
protected override void DisposeResources()
{ }
}
}
}
@@ -4,14 +4,14 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Web.Caching;
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements <see cref="IAppPolicyCache"/> on top of a <see cref="System.Web.Caching.Cache"/>.
/// A CacheProvider that wraps the logic of the HttpRuntime.Cache
/// </summary>
/// <remarks>The underlying cache is expected to be HttpRuntime.Cache.</remarks>
internal class WebCachingAppCache : FastDictionaryAppCacheBase, IAppPolicyCache
internal class HttpRuntimeCacheProvider : DictionaryCacheProviderBase, IRuntimeCacheProvider
{
// locker object that supports upgradeable read locking
// does not need to support recursion if we implement the cache correctly and ensure
@@ -21,43 +21,16 @@ namespace Umbraco.Core.Cache
private readonly System.Web.Caching.Cache _cache;
/// <summary>
/// Initializes a new instance of the <see cref="WebCachingAppCache"/> class.
/// Used for debugging
/// </summary>
public WebCachingAppCache(System.Web.Caching.Cache cache)
internal Guid InstanceId { get; private set; }
public HttpRuntimeCacheProvider(System.Web.Caching.Cache cache)
{
_cache = cache;
InstanceId = Guid.NewGuid();
}
/// <inheritdoc />
public override object Get(string key, Func<object> factory)
{
return Get(key, factory, null, dependentFiles: null);
}
/// <inheritdoc />
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
CacheDependency dependency = null;
if (dependentFiles != null && dependentFiles.Any())
{
dependency = new CacheDependency(dependentFiles);
}
return Get(key, factory, timeout, isSliding, priority, removedCallback, dependency);
}
/// <inheritdoc />
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
CacheDependency dependency = null;
if (dependentFiles != null && dependentFiles.Any())
{
dependency = new CacheDependency(dependentFiles);
}
Insert(key, factory, timeout, isSliding, priority, removedCallback, dependency);
}
#region Dictionary
protected override IEnumerable<DictionaryEntry> GetDictionaryEntries()
{
const string prefix = CacheItemPrefix + "-";
@@ -75,8 +48,6 @@ namespace Umbraco.Core.Cache
return _cache.Get(key);
}
#endregion
#region Lock
protected override void EnterReadLock()
@@ -103,9 +74,33 @@ namespace Umbraco.Core.Cache
#endregion
private object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
#region Get
/// <summary>
/// Gets (and adds if necessary) an item from the cache with all of the default parameters
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="getCacheItem"></param>
/// <returns></returns>
public override object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
key = GetCacheKey(key);
return GetCacheItem(cacheKey, getCacheItem, null, dependentFiles: null);
}
/// <summary>
/// This overload is here for legacy purposes
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="getCacheItem"></param>
/// <param name="timeout"></param>
/// <param name="isSliding"></param>
/// <param name="priority"></param>
/// <param name="removedCallback"></param>
/// <param name="dependency"></param>
/// <returns></returns>
internal object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
{
cacheKey = GetCacheKey(cacheKey);
// NOTE - because we don't know what getCacheItem does, how long it will take and whether it will hang,
// getCacheItem should run OUTSIDE of the global application lock else we run into lock contention and
@@ -138,7 +133,7 @@ namespace Umbraco.Core.Cache
try
{
_locker.EnterReadLock();
result = _cache.Get(key) as Lazy<object>; // null if key not found
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
}
finally
{
@@ -150,7 +145,7 @@ namespace Umbraco.Core.Cache
using (var lck = new UpgradeableReadLock(_locker))
{
result = _cache.Get(key) as Lazy<object>; // null if key not found
result = _cache.Get(cacheKey) as Lazy<object>; // null if key not found
// cannot create value within the lock, so if result.IsValueCreated is false, just
// do nothing here - means that if creation throws, a race condition could cause
@@ -158,13 +153,13 @@ namespace Umbraco.Core.Cache
if (result == null || GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = GetSafeLazy(factory);
result = GetSafeLazy(getCacheItem);
var absolute = isSliding ? System.Web.Caching.Cache.NoAbsoluteExpiration : (timeout == null ? System.Web.Caching.Cache.NoAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
lck.UpgradeToWriteLock();
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
_cache.Insert(key, result, dependency, absolute, sliding, priority, removedCallback);
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
}
}
@@ -180,7 +175,31 @@ namespace Umbraco.Core.Cache
return value;
}
private void Insert(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
CacheDependency dependency = null;
if (dependentFiles != null && dependentFiles.Any())
{
dependency = new CacheDependency(dependentFiles);
}
return GetCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
}
#endregion
#region Insert
/// <summary>
/// This overload is here for legacy purposes
/// </summary>
/// <param name="cacheKey"></param>
/// <param name="getCacheItem"></param>
/// <param name="timeout"></param>
/// <param name="isSliding"></param>
/// <param name="priority"></param>
/// <param name="removedCallback"></param>
/// <param name="dependency"></param>
internal void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, CacheDependency dependency = null)
{
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
// and make sure we don't store a null value.
@@ -206,5 +225,17 @@ namespace Umbraco.Core.Cache
_locker.ExitWriteLock();
}
}
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
CacheDependency dependency = null;
if (dependentFiles != null && dependentFiles.Any())
{
dependency = new CacheDependency(dependentFiles);
}
InsertCacheItem(cacheKey, getCacheItem, timeout, isSliding, priority, removedCallback, dependency);
}
#endregion
}
}
-94
View File
@@ -1,94 +0,0 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Defines an application cache.
/// </summary>
public interface IAppCache
{
/// <summary>
/// Gets an item identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
/// <returns>The item, or null if the item was not found.</returns>
object Get(string key);
/// <summary>
/// Gets or creates an item identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
/// <param name="factory">A factory function that can create the item.</param>
/// <returns>The item.</returns>
object Get(string key, Func<object> factory);
/// <summary>
/// Gets items with a key starting with the specified value.
/// </summary>
/// <param name="keyStartsWith">The StartsWith value to use in the search.</param>
/// <returns>Items matching the search.</returns>
IEnumerable<object> SearchByKey(string keyStartsWith);
/// <summary>
/// Gets items with a key matching a regular expression.
/// </summary>
/// <param name="regex">The regular expression.</param>
/// <returns>Items matching the search.</returns>
IEnumerable<object> SearchByRegex(string regex);
/// <summary>
/// Removes all items from the cache.
/// </summary>
void Clear();
/// <summary>
/// Removes an item identified by its key from the cache.
/// </summary>
/// <param name="key">The key of the item.</param>
void Clear(string key);
/// <summary>
/// Removes items of a specified type from the cache.
/// </summary>
/// <param name="typeName">The name of the type to remove.</param>
/// <remarks>
/// <para>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</para>
/// <para>Performs a case-sensitive search.</para>
/// </remarks>
void ClearOfType(string typeName);
/// <summary>
/// Removes items of a specified type from the cache.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearOfType<T>();
/// <summary>
/// Removes items of a specified type from the cache.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <param name="predicate">The predicate to satisfy.</param>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearOfType<T>(Func<string, T, bool> predicate);
/// <summary>
/// Clears items with a key starting with the specified value.
/// </summary>
/// <param name="keyStartsWith">The StartsWith value to use in the search.</param>
void ClearByKey(string keyStartsWith);
/// <summary>
/// Clears items with a key matching a regular expression.
/// </summary>
/// <param name="regex">The regular expression.</param>
void ClearByRegex(string regex);
}
}
-52
View File
@@ -1,52 +0,0 @@
using System;
using System.Web.Caching;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Defines an application cache that support cache policies.
/// </summary>
/// <remarks>A cache policy can be used to cache with timeouts,
/// or depending on files, and with a remove callback, etc.</remarks>
public interface IAppPolicyCache : IAppCache
{
/// <summary>
/// Gets an item identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
/// <param name="factory">A factory function that can create the item.</param>
/// <param name="timeout">An optional cache timeout.</param>
/// <param name="isSliding">An optional value indicating whether the cache timeout is sliding (default is false).</param>
/// <param name="priority">An optional cache priority (default is Normal).</param>
/// <param name="removedCallback">An optional callback to handle removals.</param>
/// <param name="dependentFiles">Files the cache entry depends on.</param>
/// <returns>The item.</returns>
object Get(
string key,
Func<object> factory,
TimeSpan? timeout,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
/// <summary>
/// Inserts an item.
/// </summary>
/// <param name="key">The key of the item.</param>
/// <param name="factory">A factory function that can create the item.</param>
/// <param name="timeout">An optional cache timeout.</param>
/// <param name="isSliding">An optional value indicating whether the cache timeout is sliding (default is false).</param>
/// <param name="priority">An optional cache priority (default is Normal).</param>
/// <param name="removedCallback">An optional callback to handle removals.</param>
/// <param name="dependentFiles">Files the cache entry depends on.</param>
void Insert(
string key,
Func<object> factory,
TimeSpan? timeout = null,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
}
}
+68
View File
@@ -0,0 +1,68 @@
using System;
using System.Collections.Generic;
namespace Umbraco.Core.Cache
{
/// <summary>
/// An interface for implementing a basic cache provider
/// </summary>
public interface ICacheProvider
{
/// <summary>
/// Removes all items from the cache.
/// </summary>
void ClearAllCache();
/// <summary>
/// Removes an item from the cache, identified by its key.
/// </summary>
/// <param name="key">The key of the item.</param>
void ClearCacheItem(string key);
/// <summary>
/// Removes items from the cache, of a specified type.
/// </summary>
/// <param name="typeName">The name of the type to remove.</param>
/// <remarks>
/// <para>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</para>
/// <para>Performs a case-sensitive search.</para>
/// </remarks>
void ClearCacheObjectTypes(string typeName);
/// <summary>
/// Removes items from the cache, of a specified type.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearCacheObjectTypes<T>();
/// <summary>
/// Removes items from the cache, of a specified type, satisfying a predicate.
/// </summary>
/// <typeparam name="T">The type of the items to remove.</typeparam>
/// <param name="predicate">The predicate to satisfy.</param>
/// <remarks>If the type is an interface, then all items of a type implementing that interface are
/// removed. Otherwise, only items of that exact type are removed (items of type inheriting from
/// the specified type are not removed).</remarks>
void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate);
void ClearCacheByKeySearch(string keyStartsWith);
void ClearCacheByKeyExpression(string regexString);
IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith);
IEnumerable<object> GetCacheItemsByKeyExpression(string regexString);
/// <summary>
/// Returns an item with a given key
/// </summary>
/// <param name="cacheKey"></param>
/// <returns></returns>
object GetCacheItem(string cacheKey);
object GetCacheItem(string cacheKey, Func<object> getCacheItem);
}
}
+1 -1
View File
@@ -4,7 +4,7 @@ using Umbraco.Core.Composing;
namespace Umbraco.Core.Cache
{
/// <summary>
/// The IcacheRefresher Interface is used for load balancing.
/// The IcacheRefresher Interface is used for loadbalancing.
///
/// </summary>
public interface ICacheRefresher : IDiscoverable
@@ -48,7 +48,7 @@ namespace Umbraco.Core.Cache
/// Updates an entity.
/// </summary>
/// <param name="entity">The entity.</param>
/// <param name="persistUpdated">The repository PersistUpdatedItem method.</param>
/// <param name="persistUpdated">The reopsitory PersistUpdatedItem method.</param>
/// <remarks>Updates the entity in the repository, and updates the cache accordingly.</remarks>
void Update(TEntity entity, Action<TEntity> persistUpdated);
@@ -0,0 +1,35 @@
using System;
using System.Runtime.Caching;
using System.Text;
using System.Web.Caching;
using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
{
/// <summary>
/// An abstract class for implementing a runtime cache provider
/// </summary>
/// <remarks>
/// </remarks>
public interface IRuntimeCacheProvider : ICacheProvider
{
object GetCacheItem(
string cacheKey,
Func<object> getCacheItem,
TimeSpan? timeout,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
void InsertCacheItem(
string cacheKey,
Func<object> getCacheItem,
TimeSpan? timeout = null,
bool isSliding = false,
CacheItemPriority priority = CacheItemPriority.Normal,
CacheItemRemovedCallback removedCallback = null,
string[] dependentFiles = null);
}
}
-41
View File
@@ -1,41 +0,0 @@
using System;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Represents a dictionary of <see cref="IAppPolicyCache"/> for types.
/// </summary>
/// <remarks>
/// <para>Isolated caches are used by e.g. repositories, to ensure that each cached entity
/// type has its own cache, so that lookups are fast and the repository does not need to
/// search through all keys on a global scale.</para>
/// </remarks>
public class IsolatedCaches : AppPolicedCacheDictionary<Type>
{
/// <summary>
/// Initializes a new instance of the <see cref="IsolatedCaches"/> class.
/// </summary>
/// <param name="cacheFactory"></param>
public IsolatedCaches(Func<Type, IAppPolicyCache> cacheFactory)
: base(cacheFactory)
{ }
/// <summary>
/// Gets a cache.
/// </summary>
public IAppPolicyCache GetOrCreate<T>()
=> GetOrCreate(typeof(T));
/// <summary>
/// Tries to get a cache.
/// </summary>
public Attempt<IAppPolicyCache> Get<T>()
=> Get(typeof(T));
/// <summary>
/// Clears a cache.
/// </summary>
public void ClearCache<T>()
=> ClearCache(typeof(T));
}
}
@@ -0,0 +1,91 @@
using System;
using System.Collections.Concurrent;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Used to get/create/manipulate isolated runtime cache
/// </summary>
/// <remarks>
/// This is useful for repository level caches to ensure that cache lookups by key are fast so
/// that the repository doesn't need to search through all keys on a global scale.
/// </remarks>
public class IsolatedRuntimeCache
{
internal Func<Type, IRuntimeCacheProvider> CacheFactory { get; set; }
/// <summary>
/// Constructor that allows specifying a factory for the type of runtime isolated cache to create
/// </summary>
/// <param name="cacheFactory"></param>
public IsolatedRuntimeCache(Func<Type, IRuntimeCacheProvider> cacheFactory)
{
CacheFactory = cacheFactory;
}
private readonly ConcurrentDictionary<Type, IRuntimeCacheProvider> _isolatedCache = new ConcurrentDictionary<Type, IRuntimeCacheProvider>();
/// <summary>
/// Returns an isolated runtime cache for a given type
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public IRuntimeCacheProvider GetOrCreateCache<T>()
{
return _isolatedCache.GetOrAdd(typeof(T), type => CacheFactory(type));
}
/// <summary>
/// Returns an isolated runtime cache for a given type
/// </summary>
/// <returns></returns>
public IRuntimeCacheProvider GetOrCreateCache(Type type)
{
return _isolatedCache.GetOrAdd(type, t => CacheFactory(t));
}
/// <summary>
/// Tries to get a cache by the type specified
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public Attempt<IRuntimeCacheProvider> GetCache<T>()
{
IRuntimeCacheProvider cache;
if (_isolatedCache.TryGetValue(typeof(T), out cache))
{
return Attempt.Succeed(cache);
}
return Attempt<IRuntimeCacheProvider>.Fail();
}
/// <summary>
/// Clears all values inside this isolated runtime cache
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
public void ClearCache<T>()
{
IRuntimeCacheProvider cache;
if (_isolatedCache.TryGetValue(typeof(T), out cache))
{
cache.ClearAllCache();
}
}
/// <summary>
/// Clears all of the isolated caches
/// </summary>
public void ClearAllCaches()
{
foreach (var key in _isolatedCache.Keys)
{
IRuntimeCacheProvider cache;
if (_isolatedCache.TryRemove(key, out cache))
{
cache.ClearAllCache();
}
}
}
}
}
@@ -13,8 +13,8 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Initializes a new instance of the <see cref="JsonCacheRefresherBase{TInstanceType}"/>.
/// </summary>
/// <param name="appCaches">A cache helper.</param>
protected JsonCacheRefresherBase(AppCaches appCaches) : base(appCaches)
/// <param name="cacheHelper">A cache helper.</param>
protected JsonCacheRefresherBase(CacheHelper cacheHelper) : base(cacheHelper)
{ }
/// <summary>
-82
View File
@@ -1,82 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements <see cref="IAppPolicyCache"/> and do not cache.
/// </summary>
public class NoAppCache : IAppPolicyCache
{
private NoAppCache() { }
/// <summary>
/// Gets the singleton instance.
/// </summary>
public static NoAppCache Instance { get; } = new NoAppCache();
/// <inheritdoc />
public virtual object Get(string cacheKey)
{
return null;
}
/// <inheritdoc />
public virtual object Get(string cacheKey, Func<object> factory)
{
return factory();
}
/// <inheritdoc />
public virtual IEnumerable<object> SearchByKey(string keyStartsWith)
{
return Enumerable.Empty<object>();
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
return Enumerable.Empty<object>();
}
/// <inheritdoc />
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
return factory();
}
/// <inheritdoc />
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{ }
/// <inheritdoc />
public virtual void Clear()
{ }
/// <inheritdoc />
public virtual void Clear(string key)
{ }
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
{ }
/// <inheritdoc />
public virtual void ClearOfType<T>()
{ }
/// <inheritdoc />
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
{ }
/// <inheritdoc />
public virtual void ClearByKey(string keyStartsWith)
{ }
/// <inheritdoc />
public virtual void ClearByRegex(string regex)
{ }
}
}
@@ -13,7 +13,7 @@ namespace Umbraco.Core.Cache
public static NoCacheRepositoryCachePolicy<TEntity, TId> Instance { get; } = new NoCacheRepositoryCachePolicy<TEntity, TId>();
public IRepositoryCachePolicy<TEntity, TId> Scoped(IAppPolicyCache runtimeCache, IScope scope)
public IRepositoryCachePolicy<TEntity, TId> Scoped(IRuntimeCacheProvider runtimeCache, IScope scope)
{
throw new NotImplementedException();
}
@@ -0,0 +1,66 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Caching;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Represents a cache provider that does not cache anything.
/// </summary>
public class NullCacheProvider : IRuntimeCacheProvider
{
private NullCacheProvider() { }
public static NullCacheProvider Instance { get; } = new NullCacheProvider();
public virtual void ClearAllCache()
{ }
public virtual void ClearCacheItem(string key)
{ }
public virtual void ClearCacheObjectTypes(string typeName)
{ }
public virtual void ClearCacheObjectTypes<T>()
{ }
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{ }
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{ }
public virtual void ClearCacheByKeyExpression(string regexString)
{ }
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return Enumerable.Empty<object>();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return Enumerable.Empty<object>();
}
public virtual object GetCacheItem(string cacheKey)
{
return default(object);
}
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return getCacheItem();
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
return getCacheItem();
}
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{ }
}
}
@@ -11,142 +11,31 @@ using CacheItemPriority = System.Web.Caching.CacheItemPriority;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Implements <see cref="IAppPolicyCache"/> on top of a <see cref="ObjectCache"/>.
/// Represents a cache provider that caches item in a <see cref="MemoryCache"/>.
/// A cache provider that wraps the logic of a System.Runtime.Caching.ObjectCache
/// </summary>
public class ObjectCacheAppCache : IAppPolicyCache
/// <remarks>The <see cref="MemoryCache"/> is created with name "in-memory". That name is
/// used to retrieve configuration options. It does not identify the memory cache, i.e.
/// each instance of this class has its own, independent, memory cache.</remarks>
public class ObjectCacheRuntimeCacheProvider : IRuntimeCacheProvider
{
private readonly ReaderWriterLockSlim _locker = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
internal ObjectCache MemoryCache;
/// <summary>
/// Initializes a new instance of the <see cref="ObjectCacheAppCache"/>.
/// Used for debugging
/// </summary>
public ObjectCacheAppCache()
internal Guid InstanceId { get; private set; }
public ObjectCacheRuntimeCacheProvider()
{
// the MemoryCache is created with name "in-memory". That name is
// used to retrieve configuration options. It does not identify the memory cache, i.e.
// each instance of this class has its own, independent, memory cache.
MemoryCache = new MemoryCache("in-memory");
InstanceId = Guid.NewGuid();
}
/// <summary>
/// Gets the internal memory cache, for tests only!
/// </summary>
internal ObjectCache MemoryCache { get; private set; }
#region Clear
/// <inheritdoc />
public object Get(string key)
{
Lazy<object> result;
try
{
_locker.EnterReadLock();
result = MemoryCache.Get(key) as Lazy<object>; // null if key not found
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return result == null ? null : FastDictionaryAppCacheBase.GetSafeLazyValue(result); // return exceptions as null
}
/// <inheritdoc />
public object Get(string key, Func<object> factory)
{
return Get(key, factory, null);
}
/// <inheritdoc />
public IEnumerable<object> SearchByKey(string keyStartsWith)
{
KeyValuePair<string, object>[] entries;
try
{
_locker.EnterReadLock();
entries = MemoryCache
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return entries
.Select(x => FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
/// <inheritdoc />
public IEnumerable<object> SearchByRegex(string regex)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
KeyValuePair<string, object>[] entries;
try
{
_locker.EnterReadLock();
entries = MemoryCache
.Where(x => compiled.IsMatch(x.Key))
.ToArray(); // evaluate while locked
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return entries
.Select(x => FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
/// <inheritdoc />
public object Get(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
// see notes in HttpRuntimeAppCache
Lazy<object> result;
using (var lck = new UpgradeableReadLock(_locker))
{
result = MemoryCache.Get(key) as Lazy<object>;
if (result == null || FastDictionaryAppCacheBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
lck.UpgradeToWriteLock();
//NOTE: This does an add or update
MemoryCache.Set(key, result, policy);
}
}
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
if (value is FastDictionaryAppCacheBase.ExceptionHolder eh) eh.Exception.Throw(); // throw once!
return value;
}
/// <inheritdoc />
public void Insert(string key, Func<object> factory, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
// and make sure we don't store a null value.
var result = FastDictionaryAppCacheBase.GetSafeLazy(factory);
var value = result.Value; // force evaluation now
if (value == null) return; // do not store null values (backward compat)
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
//NOTE: This does an add or update
MemoryCache.Set(key, result, policy);
}
/// <inheritdoc />
public virtual void Clear()
public virtual void ClearAllCache()
{
try
{
@@ -161,8 +50,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void Clear(string key)
public virtual void ClearCacheItem(string key)
{
try
{
@@ -177,8 +65,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType(string typeName)
public virtual void ClearCacheObjectTypes(string typeName)
{
var type = TypeFinder.GetTypeByName(typeName);
if (type == null) return;
@@ -192,7 +79,7 @@ namespace Umbraco.Core.Cache
// x.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
@@ -209,13 +96,12 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType<T>()
public virtual void ClearCacheObjectTypes<T>()
{
try
{
_locker.EnterWriteLock();
var typeOfT = typeof(T);
var typeOfT = typeof (T);
var isInterface = typeOfT.IsInterface;
foreach (var key in MemoryCache
.Where(x =>
@@ -223,7 +109,7 @@ namespace Umbraco.Core.Cache
// x.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
// if T is an interface remove anything that implements that interface
// otherwise remove exact types (not inherited types)
@@ -241,8 +127,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearOfType<T>(Func<string, T, bool> predicate)
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
try
{
@@ -255,7 +140,7 @@ namespace Umbraco.Core.Cache
// x.Value is Lazy<object> and not null, its value may be null
// remove null values as well, does not hurt
// get non-created as NonCreatedValue & exceptions as null
var value = FastDictionaryAppCacheBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
var value = DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value, true);
if (value == null) return true;
// if T is an interface remove anything that implements that interface
@@ -274,8 +159,7 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearByKey(string keyStartsWith)
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
try
{
@@ -293,16 +177,13 @@ namespace Umbraco.Core.Cache
}
}
/// <inheritdoc />
public virtual void ClearByRegex(string regex)
public virtual void ClearCacheByKeyExpression(string regexString)
{
var compiled = new Regex(regex, RegexOptions.Compiled);
try
{
_locker.EnterWriteLock();
foreach (var key in MemoryCache
.Where(x => compiled.IsMatch(x.Key))
.Where(x => Regex.IsMatch(x.Key, regexString))
.Select(x => x.Key)
.ToArray()) // ToArray required to remove
MemoryCache.Remove(key);
@@ -314,6 +195,120 @@ namespace Umbraco.Core.Cache
}
}
#endregion
#region Get
public IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
KeyValuePair<string, object>[] entries;
try
{
_locker.EnterReadLock();
entries = MemoryCache
.Where(x => x.Key.InvariantStartsWith(keyStartsWith))
.ToArray(); // evaluate while locked
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return entries
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
KeyValuePair<string, object>[] entries;
try
{
_locker.EnterReadLock();
entries = MemoryCache
.Where(x => Regex.IsMatch(x.Key, regexString))
.ToArray(); // evaluate while locked
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return entries
.Select(x => DictionaryCacheProviderBase.GetSafeLazyValue((Lazy<object>)x.Value)) // return exceptions as null
.Where(x => x != null) // backward compat, don't store null values in the cache
.ToList();
}
public object GetCacheItem(string cacheKey)
{
Lazy<object> result;
try
{
_locker.EnterReadLock();
result = MemoryCache.Get(cacheKey) as Lazy<object>; // null if key not found
}
finally
{
if (_locker.IsReadLockHeld)
_locker.ExitReadLock();
}
return result == null ? null : DictionaryCacheProviderBase.GetSafeLazyValue(result); // return exceptions as null
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return GetCacheItem(cacheKey, getCacheItem, null);
}
public object GetCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
// see notes in HttpRuntimeCacheProvider
Lazy<object> result;
using (var lck = new UpgradeableReadLock(_locker))
{
result = MemoryCache.Get(cacheKey) as Lazy<object>;
if (result == null || DictionaryCacheProviderBase.GetSafeLazyValue(result, true) == null) // get non-created as NonCreatedValue & exceptions as null
{
result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
lck.UpgradeToWriteLock();
//NOTE: This does an add or update
MemoryCache.Set(cacheKey, result, policy);
}
}
//return result.Value;
var value = result.Value; // will not throw (safe lazy)
if (value is DictionaryCacheProviderBase.ExceptionHolder eh) eh.Exception.Throw(); // throw once!
return value;
}
#endregion
#region Insert
public void InsertCacheItem(string cacheKey, Func<object> getCacheItem, TimeSpan? timeout = null, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
// NOTE - here also we must insert a Lazy<object> but we can evaluate it right now
// and make sure we don't store a null value.
var result = DictionaryCacheProviderBase.GetSafeLazy(getCacheItem);
var value = result.Value; // force evaluation now
if (value == null) return; // do not store null values (backward compat)
var policy = GetPolicy(timeout, isSliding, removedCallback, dependentFiles);
//NOTE: This does an add or update
MemoryCache.Set(cacheKey, result, policy);
}
#endregion
private static CacheItemPolicy GetPolicy(TimeSpan? timeout = null, bool isSliding = false, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
{
var absolute = isSliding ? ObjectCache.InfiniteAbsoluteExpiration : (timeout == null ? ObjectCache.InfiniteAbsoluteExpiration : DateTime.Now.Add(timeout.Value));
@@ -15,8 +15,8 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Initializes a new instance of the <see cref="PayloadCacheRefresherBase{TInstanceType, TPayload}"/>.
/// </summary>
/// <param name="appCaches">A cache helper.</param>
protected PayloadCacheRefresherBase(AppCaches appCaches) : base(appCaches)
/// <param name="cacheHelper">A cache helper.</param>
protected PayloadCacheRefresherBase(CacheHelper cacheHelper) : base(cacheHelper)
{ }
#region Json
@@ -13,16 +13,16 @@ namespace Umbraco.Core.Cache
internal abstract class RepositoryCachePolicyBase<TEntity, TId> : IRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IEntity
{
private readonly IAppPolicyCache _globalCache;
private readonly IRuntimeCacheProvider _globalCache;
private readonly IScopeAccessor _scopeAccessor;
protected RepositoryCachePolicyBase(IAppPolicyCache globalCache, IScopeAccessor scopeAccessor)
protected RepositoryCachePolicyBase(IRuntimeCacheProvider globalCache, IScopeAccessor scopeAccessor)
{
_globalCache = globalCache ?? throw new ArgumentNullException(nameof(globalCache));
_scopeAccessor = scopeAccessor ?? throw new ArgumentNullException(nameof(scopeAccessor));
}
protected IAppPolicyCache Cache
protected IRuntimeCacheProvider Cache
{
get
{
@@ -32,9 +32,9 @@ namespace Umbraco.Core.Cache
case RepositoryCacheMode.Default:
return _globalCache;
case RepositoryCacheMode.Scoped:
return ambientScope.IsolatedCaches.GetOrCreate<TEntity>();
return ambientScope.IsolatedRuntimeCache.GetOrCreateCache<TEntity>();
case RepositoryCacheMode.None:
return NoAppCache.Instance;
return NullCacheProvider.Instance;
default:
throw new NotSupportedException($"Repository cache mode {ambientScope.RepositoryCacheMode} is not supported.");
}
@@ -16,7 +16,7 @@ namespace Umbraco.Core.Cache
internal class SingleItemsOnlyRepositoryCachePolicy<TEntity, TId> : DefaultRepositoryCachePolicy<TEntity, TId>
where TEntity : class, IEntity
{
public SingleItemsOnlyRepositoryCachePolicy(IAppPolicyCache cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
public SingleItemsOnlyRepositoryCachePolicy(IRuntimeCacheProvider cache, IScopeAccessor scopeAccessor, RepositoryCachePolicyOptions options)
: base(cache, scopeAccessor, options)
{ }
@@ -0,0 +1,79 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
namespace Umbraco.Core.Cache
{
/// <summary>
/// Represents a cache provider that statically caches item in a concurrent dictionary.
/// </summary>
public class StaticCacheProvider : ICacheProvider
{
internal readonly ConcurrentDictionary<string, object> StaticCache = new ConcurrentDictionary<string, object>();
public virtual void ClearAllCache()
{
StaticCache.Clear();
}
public virtual void ClearCacheItem(string key)
{
object val;
StaticCache.TryRemove(key, out val);
}
public virtual void ClearCacheObjectTypes(string typeName)
{
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType().ToString().InvariantEquals(typeName));
}
public virtual void ClearCacheObjectTypes<T>()
{
var typeOfT = typeof(T);
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT);
}
public virtual void ClearCacheObjectTypes<T>(Func<string, T, bool> predicate)
{
var typeOfT = typeof(T);
StaticCache.RemoveAll(kvp => kvp.Value != null && kvp.Value.GetType() == typeOfT && predicate(kvp.Key, (T)kvp.Value));
}
public virtual void ClearCacheByKeySearch(string keyStartsWith)
{
StaticCache.RemoveAll(kvp => kvp.Key.InvariantStartsWith(keyStartsWith));
}
public virtual void ClearCacheByKeyExpression(string regexString)
{
StaticCache.RemoveAll(kvp => Regex.IsMatch(kvp.Key, regexString));
}
public virtual IEnumerable<object> GetCacheItemsByKeySearch(string keyStartsWith)
{
return (from KeyValuePair<string, object> c in StaticCache
where c.Key.InvariantStartsWith(keyStartsWith)
select c.Value).ToList();
}
public IEnumerable<object> GetCacheItemsByKeyExpression(string regexString)
{
return (from KeyValuePair<string, object> c in StaticCache
where Regex.IsMatch(c.Key, regexString)
select c.Value).ToList();
}
public virtual object GetCacheItem(string cacheKey)
{
var result = StaticCache[cacheKey];
return result;
}
public virtual object GetCacheItem(string cacheKey, Func<object> getCacheItem)
{
return StaticCache.GetOrAdd(cacheKey, key => getCacheItem());
}
}
}
@@ -14,9 +14,9 @@ namespace Umbraco.Core.Cache
/// <summary>
/// Initializes a new instance of the <see cref="TypedCacheRefresherBase{TInstanceType, TEntityType}"/>.
/// </summary>
/// <param name="appCaches">A cache helper.</param>
protected TypedCacheRefresherBase(AppCaches appCaches)
: base(appCaches)
/// <param name="cacheHelper">A cache helper.</param>
protected TypedCacheRefresherBase(CacheHelper cacheHelper)
: base(cacheHelper)
{ }
#region Refresher
@@ -0,0 +1,35 @@
using System;
using Umbraco.Core.Exceptions;
namespace Umbraco.Core.CodeAnnotations
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
internal class ActionMetadataAttribute : Attribute
{
public string Category { get; }
public string Name { get; }
/// <summary>
/// Constructor used to assign a Category, since no name is assigned it will try to be translated from the language files based on the action's alias
/// </summary>
/// <param name="category"></param>
public ActionMetadataAttribute(string category)
{
if (string.IsNullOrWhiteSpace(category)) throw new ArgumentNullOrEmptyException(nameof(category));
Category = category;
}
/// <summary>
/// Constructor used to assign an explicit name and category
/// </summary>
/// <param name="category"></param>
/// <param name="name"></param>
public ActionMetadataAttribute(string category, string name)
{
if (string.IsNullOrWhiteSpace(category)) throw new ArgumentNullOrEmptyException(nameof(category));
if (string.IsNullOrWhiteSpace(name)) throw new ArgumentNullOrEmptyException(nameof(name));
Category = category;
Name = name;
}
}
}
@@ -40,4 +40,4 @@ namespace Umbraco.Core.Collections
public static bool operator !=(CompositeIntStringKey key1, CompositeIntStringKey key2)
=> key1._key2 != key2._key2 || key1._key1 != key2._key1;
}
}
}
@@ -70,7 +70,10 @@ namespace Umbraco.Core.Collections
/// The number of elements contained in the <see cref="T:System.Collections.ICollection"/>.
/// </returns>
/// <filterpriority>2</filterpriority>
public int Count => GetThreadSafeClone().Count;
public int Count
{
get { return GetThreadSafeClone().Count; }
}
/// <summary>
/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
@@ -78,7 +81,10 @@ namespace Umbraco.Core.Collections
/// <returns>
/// true if the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only; otherwise, false.
/// </returns>
public bool IsReadOnly => false;
public bool IsReadOnly
{
get { return false; }
}
/// <summary>
/// Adds an item to the <see cref="T:System.Collections.Generic.ICollection`1"/>.
@@ -14,31 +14,30 @@ namespace Umbraco.Core.Collections
/// </remarks>
/// <typeparam name="TValue">The type of elements contained in the BindableCollection</typeparam>
/// <typeparam name="TKey">The type of the indexing key</typeparam>
public class ObservableDictionary<TKey, TValue> : ObservableCollection<TValue>, IReadOnlyDictionary<TKey, TValue>, IDictionary<TKey, TValue>
public class ObservableDictionary<TKey, TValue> : ObservableCollection<TValue>
{
protected Dictionary<TKey, int> Indecies { get; }
protected Func<TValue, TKey> KeySelector { get; }
protected Dictionary<TKey, int> Indecies = new Dictionary<TKey, int>();
protected Func<TValue, TKey> KeySelector;
/// <summary>
/// Create new ObservableDictionary
/// </summary>
/// <param name="keySelector">Selector function to create key from value</param>
/// <param name="equalityComparer">The equality comparer to use when comparing keys, or null to use the default comparer.</param>
public ObservableDictionary(Func<TValue, TKey> keySelector, IEqualityComparer<TKey> equalityComparer = null)
public ObservableDictionary(Func<TValue, TKey> keySelector)
: base()
{
KeySelector = keySelector ?? throw new ArgumentException("keySelector");
Indecies = new Dictionary<TKey, int>(equalityComparer);
if (keySelector == null) throw new ArgumentException("keySelector");
KeySelector = keySelector;
}
#region Protected Methods
protected override void InsertItem(int index, TValue item)
{
var key = KeySelector(item);
if (Indecies.ContainsKey(key))
throw new DuplicateKeyException(key.ToString());
if (index != Count)
if (index != this.Count)
{
foreach (var k in Indecies.Keys.Where(k => Indecies[k] >= index).ToList())
{
@@ -48,6 +47,7 @@ namespace Umbraco.Core.Collections
base.InsertItem(index, item);
Indecies[key] = index;
}
protected override void ClearItems()
@@ -56,6 +56,7 @@ namespace Umbraco.Core.Collections
Indecies.Clear();
}
protected override void RemoveItem(int index)
{
var item = this[index];
@@ -70,10 +71,9 @@ namespace Umbraco.Core.Collections
Indecies[k]--;
}
}
#endregion
public bool ContainsKey(TKey key)
public virtual bool ContainsKey(TKey key)
{
return Indecies.ContainsKey(key);
}
@@ -83,10 +83,10 @@ namespace Umbraco.Core.Collections
/// </summary>
/// <param name="key">Key of element to replace</param>
/// <returns></returns>
public TValue this[TKey key]
public virtual TValue this[TKey key]
{
get => this[Indecies[key]];
get { return this[Indecies[key]]; }
set
{
//confirm key matches
@@ -95,7 +95,7 @@ namespace Umbraco.Core.Collections
if (!Indecies.ContainsKey(key))
{
Add(value);
this.Add(value);
}
else
{
@@ -112,10 +112,9 @@ namespace Umbraco.Core.Collections
///
/// <exception cref="InvalidOperationException"></exception>
/// <returns>False if key not found</returns>
public bool Replace(TKey key, TValue value)
public virtual bool Replace(TKey key, TValue value)
{
if (!Indecies.ContainsKey(key)) return false;
//confirm key matches
if (!KeySelector(value).Equals(key))
throw new InvalidOperationException("Key of new value does not match");
@@ -125,23 +124,11 @@ namespace Umbraco.Core.Collections
}
public void ReplaceAll(IEnumerable<TValue> values)
{
if (values == null) throw new ArgumentNullException(nameof(values));
Clear();
foreach (var value in values)
{
Add(value);
}
}
public bool Remove(TKey key)
public virtual bool Remove(TKey key)
{
if (!Indecies.ContainsKey(key)) return false;
RemoveAt(Indecies[key]);
this.RemoveAt(Indecies[key]);
return true;
}
@@ -151,13 +138,12 @@ namespace Umbraco.Core.Collections
/// </summary>
/// <param name="currentKey"></param>
/// <param name="newKey"></param>
public void ChangeKey(TKey currentKey, TKey newKey)
public virtual void ChangeKey(TKey currentKey, TKey newKey)
{
if (!Indecies.ContainsKey(currentKey))
{
throw new InvalidOperationException("No item with the key " + currentKey + "was found in the collection");
}
if (ContainsKey(newKey))
{
throw new DuplicateKeyException(newKey.ToString());
@@ -169,81 +155,16 @@ namespace Umbraco.Core.Collections
Indecies.Add(newKey, currentIndex);
}
#region IDictionary and IReadOnlyDictionary implementation
public bool TryGetValue(TKey key, out TValue val)
{
if (Indecies.TryGetValue(key, out var index))
{
val = this[index];
return true;
}
val = default;
return false;
}
/// <summary>
/// Returns all keys
/// </summary>
public IEnumerable<TKey> Keys => Indecies.Keys;
/// <summary>
/// Returns all values
/// </summary>
public IEnumerable<TValue> Values => base.Items;
ICollection<TKey> IDictionary<TKey, TValue>.Keys => Indecies.Keys;
//this will never be used
ICollection<TValue> IDictionary<TKey, TValue>.Values => Values.ToList();
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
{
foreach (var i in Values)
{
var key = KeySelector(i);
yield return new KeyValuePair<TKey, TValue>(key, i);
}
}
void IDictionary<TKey, TValue>.Add(TKey key, TValue value)
{
Add(value);
}
void ICollection<KeyValuePair<TKey, TValue>>.Add(KeyValuePair<TKey, TValue> item)
{
Add(item.Value);
}
bool ICollection<KeyValuePair<TKey, TValue>>.Contains(KeyValuePair<TKey, TValue> item)
{
return ContainsKey(item.Key);
}
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
{
throw new NotImplementedException();
}
bool ICollection<KeyValuePair<TKey, TValue>>.Remove(KeyValuePair<TKey, TValue> item)
{
return Remove(item.Key);
}
#endregion
internal class DuplicateKeyException : Exception
{
public string Key { get; private set; }
public DuplicateKeyException(string key)
: base("Attempted to insert duplicate key \"" + key + "\" in collection.")
: base("Attempted to insert duplicate key " + key + " in collection")
{
Key = key;
}
public string Key { get; }
}
}
}
+368
View File
@@ -0,0 +1,368 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using LightInject;
using Umbraco.Core.Collections;
using Umbraco.Core.Exceptions;
using Umbraco.Core.Logging;
using Umbraco.Core.Scoping;
namespace Umbraco.Core.Components
{
// note: this class is NOT thread-safe in any ways
internal class BootLoader
{
private readonly IServiceContainer _container;
private readonly ProfilingLogger _proflog;
private readonly ILogger _logger;
private IUmbracoComponent[] _components;
private bool _booted;
private const int LogThresholdMilliseconds = 100;
/// <summary>
/// Initializes a new instance of the <see cref="BootLoader"/> class.
/// </summary>
/// <param name="container">The application container.</param>
public BootLoader(IServiceContainer container)
{
_container = container ?? throw new ArgumentNullException(nameof(container));
_proflog = container.GetInstance<ProfilingLogger>();
_logger = container.GetInstance<ILogger>();
}
private class EnableInfo
{
public bool Enabled;
public int Weight = -1;
}
public void Boot(IEnumerable<Type> componentTypes, RuntimeLevel level)
{
if (_booted) throw new InvalidOperationException("Can not boot, has already booted.");
var orderedComponentTypes = PrepareComponentTypes(componentTypes, level);
InstanciateComponents(orderedComponentTypes);
ComposeComponents(level);
using (var scope = _container.GetInstance<IScopeProvider>().CreateScope())
{
InitializeComponents();
scope.Complete();
}
// rejoice!
_booted = true;
}
private IEnumerable<Type> PrepareComponentTypes(IEnumerable<Type> componentTypes, RuntimeLevel level)
{
using (_proflog.DebugDuration<BootLoader>("Preparing component types.", "Prepared component types."))
{
return PrepareComponentTypes2(componentTypes, level);
}
}
private IEnumerable<Type> PrepareComponentTypes2(IEnumerable<Type> componentTypes, RuntimeLevel level)
{
// create a list, remove those that cannot be enabled due to runtime level
var componentTypeList = componentTypes
.Where(x =>
{
// use the min level specified by the attribute if any
// otherwise, user components have Run min level, anything else is Unknown (always run)
var attr = x.GetCustomAttribute<RuntimeLevelAttribute>();
var minLevel = attr?.MinLevel ?? (x.Implements<IUmbracoUserComponent>() ? RuntimeLevel.Run : RuntimeLevel.Unknown);
return level >= minLevel;
})
.ToList();
// cannot remove that one - ever
if (componentTypeList.Contains(typeof(UmbracoCoreComponent)) == false)
componentTypeList.Add(typeof(UmbracoCoreComponent));
// enable or disable components
EnableDisableComponents(componentTypeList);
// sort the components according to their dependencies
var requirements = new Dictionary<Type, List<Type>>();
foreach (var type in componentTypeList) requirements[type] = null;
foreach (var type in componentTypeList)
{
GatherRequirementsFromRequireAttribute(type, componentTypeList, requirements);
GatherRequirementsFromRequiredAttribute(type, componentTypeList, requirements);
}
// only for debugging, this is verbose
//_logger.Debug<BootLoader>(GetComponentsReport(requirements));
// sort components
var graph = new TopoGraph<Type, KeyValuePair<Type, List<Type>>>(kvp => kvp.Key, kvp => kvp.Value);
graph.AddItems(requirements);
List<Type> sortedComponentTypes;
try
{
sortedComponentTypes = graph.GetSortedItems().Select(x => x.Key).ToList();
}
catch (Exception e)
{
// in case of an error, force-dump everything to log
_logger.Info<BootLoader>(() => GetComponentsReport(requirements));
_logger.Error<BootLoader>("Failed to sort components.", e);
throw;
}
// bit verbose but should help for troubleshooting
var text = "Ordered Components: " + Environment.NewLine + string.Join(Environment.NewLine, sortedComponentTypes) + Environment.NewLine;
Console.WriteLine(text);
_logger.Debug<BootLoader>(text);
return sortedComponentTypes;
}
private static string GetComponentsReport(Dictionary<Type, List<Type>> requirements)
{
var text = new StringBuilder();
text.AppendLine("Components & Dependencies:");
text.AppendLine();
foreach (var kvp in requirements)
{
var type = kvp.Key;
text.AppendLine(type.FullName);
foreach (var attribute in type.GetCustomAttributes<RequireComponentAttribute>())
text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue
? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")"))
: ""));
foreach (var attribute in type.GetCustomAttributes<RequiredComponentAttribute>())
text.AppendLine(" -< " + attribute.RequiringType);
foreach (var i in type.GetInterfaces())
{
text.AppendLine(" : " + i.FullName);
foreach (var attribute in i.GetCustomAttributes<RequireComponentAttribute>())
text.AppendLine(" -> " + attribute.RequiredType + (attribute.Weak.HasValue
? (attribute.Weak.Value ? " (weak)" : (" (strong" + (requirements.ContainsKey(attribute.RequiredType) ? ", missing" : "") + ")"))
: ""));
foreach (var attribute in i.GetCustomAttributes<RequiredComponentAttribute>())
text.AppendLine(" -< " + attribute.RequiringType);
}
if (kvp.Value != null)
foreach (var t in kvp.Value)
text.AppendLine(" = " + t);
text.AppendLine();
}
text.AppendLine("/");
text.AppendLine();
return text.ToString();
}
private static void EnableDisableComponents(ICollection<Type> types)
{
var enabled = new Dictionary<Type, EnableInfo>();
// process the enable/disable attributes
// these two attributes are *not* inherited and apply to *classes* only (not interfaces).
// remote declarations (when a component enables/disables *another* component)
// have priority over local declarations (when a component disables itself) so that
// ppl can enable components that, by default, are disabled.
// what happens in case of conflicting remote declarations is unspecified. more
// precisely, the last declaration to be processed wins, but the order of the
// declarations depends on the type finder and is unspecified.
foreach (var componentType in types)
{
foreach (var attr in componentType.GetCustomAttributes<EnableComponentAttribute>())
{
var type = attr.EnabledType ?? componentType;
if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo();
var weight = type == componentType ? 1 : 2;
if (enableInfo.Weight > weight) continue;
enableInfo.Enabled = true;
enableInfo.Weight = weight;
}
foreach (var attr in componentType.GetCustomAttributes<DisableComponentAttribute>())
{
var type = attr.DisabledType ?? componentType;
if (type == typeof(UmbracoCoreComponent)) throw new InvalidOperationException("Cannot disable UmbracoCoreComponent.");
if (enabled.TryGetValue(type, out var enableInfo) == false) enableInfo = enabled[type] = new EnableInfo();
var weight = type == componentType ? 1 : 2;
if (enableInfo.Weight > weight) continue;
enableInfo.Enabled = false;
enableInfo.Weight = weight;
}
}
// remove components that end up being disabled
foreach (var kvp in enabled.Where(x => x.Value.Enabled == false))
types.Remove(kvp.Key);
}
private static void GatherRequirementsFromRequireAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
{
// get 'require' attributes
// these attributes are *not* inherited because we want to "custom-inherit" for interfaces only
var requireAttributes = type
.GetInterfaces().SelectMany(x => x.GetCustomAttributes<RequireComponentAttribute>()) // those marking interfaces
.Concat(type.GetCustomAttributes<RequireComponentAttribute>()); // those marking the component
// what happens in case of conflicting attributes (different strong/weak for same type) is not specified.
foreach (var attr in requireAttributes)
{
if (attr.RequiredType == type) continue; // ignore self-requirements (+ exclude in implems, below)
// requiring an interface = require any enabled component implementing that interface
// unless strong, and then require at least one enabled component implementing that interface
if (attr.RequiredType.IsInterface)
{
var implems = types.Where(x => x != type && attr.RequiredType.IsAssignableFrom(x)).ToList();
if (implems.Count > 0)
{
if (requirements[type] == null) requirements[type] = new List<Type>();
requirements[type].AddRange(implems);
}
else if (attr.Weak == false) // if explicitely set to !weak, is strong, else is weak
throw new Exception($"Broken component dependency: {type.FullName} -> {attr.RequiredType.FullName}.");
}
// requiring a class = require that the component is enabled
// unless weak, and then requires it if it is enabled
else
{
if (types.Contains(attr.RequiredType))
{
if (requirements[type] == null) requirements[type] = new List<Type>();
requirements[type].Add(attr.RequiredType);
}
else if (attr.Weak != true) // if not explicitely set to weak, is strong
throw new Exception($"Broken component dependency: {type.FullName} -> {attr.RequiredType.FullName}.");
}
}
}
private static void GatherRequirementsFromRequiredAttribute(Type type, ICollection<Type> types, IDictionary<Type, List<Type>> requirements)
{
// get 'required' attributes
// fixme explain
var requiredAttributes = type
.GetInterfaces().SelectMany(x => x.GetCustomAttributes<RequiredComponentAttribute>())
.Concat(type.GetCustomAttributes<RequiredComponentAttribute>());
foreach (var attr in requiredAttributes)
{
if (attr.RequiringType == type) continue; // ignore self-requirements (+ exclude in implems, below)
if (attr.RequiringType.IsInterface)
{
var implems = types.Where(x => x != type && attr.RequiringType.IsAssignableFrom(x)).ToList();
foreach (var implem in implems)
{
if (requirements[implem] == null) requirements[implem] = new List<Type>();
requirements[implem].Add(type);
}
}
else
{
if (types.Contains(attr.RequiringType))
{
if (requirements[attr.RequiringType] == null) requirements[attr.RequiringType] = new List<Type>();
requirements[attr.RequiringType].Add(type);
}
}
}
}
private void InstanciateComponents(IEnumerable<Type> types)
{
using (_proflog.DebugDuration<BootLoader>("Instanciating components.", "Instanciated components."))
{
_components = types.Select(x => (IUmbracoComponent) Activator.CreateInstance(x)).ToArray();
}
}
private void ComposeComponents(RuntimeLevel level)
{
using (_proflog.DebugDuration<BootLoader>($"Composing components. (log when >{LogThresholdMilliseconds}ms)", "Composed components."))
{
var composition = new Composition(_container, level);
foreach (var component in _components)
{
var componentType = component.GetType();
using (_proflog.DebugDuration<BootLoader>($"Composing {componentType.FullName}.", $"Composed {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
{
component.Compose(composition);
}
}
}
}
private void InitializeComponents()
{
// use a container scope to ensure that PerScope instances are disposed
// components that require instances that should not survive should register them with PerScope lifetime
using (_proflog.DebugDuration<BootLoader>($"Initializing components. (log when >{LogThresholdMilliseconds}ms)", "Initialized components."))
using (_container.BeginScope())
{
foreach (var component in _components)
{
var componentType = component.GetType();
var initializers = componentType.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
.Where(x => x.Name == "Initialize" && x.IsGenericMethod == false);
using (_proflog.DebugDuration<BootLoader>($"Initializing {componentType.FullName}.", $"Initialised {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
{
foreach (var initializer in initializers)
{
var parameters = initializer.GetParameters()
.Select(x => GetParameter(componentType, x.ParameterType))
.ToArray();
initializer.Invoke(component, parameters);
}
}
}
}
}
private object GetParameter(Type componentType, Type parameterType)
{
object param;
try
{
param = _container.TryGetInstance(parameterType);
}
catch (Exception e)
{
throw new BootFailedException($"Could not get parameter of type {parameterType.FullName} for component {componentType.FullName}.", e);
}
if (param == null) throw new BootFailedException($"Could not get parameter of type {parameterType.FullName} for component {componentType.FullName}.");
return param;
}
public void Terminate()
{
if (_booted == false)
{
_proflog.Logger.Warn<BootLoader>("Cannot terminate, has not booted.");
return;
}
using (_proflog.DebugDuration<BootLoader>($"Terminating. (log components when >{LogThresholdMilliseconds}ms)", "Terminated."))
{
for (var i = _components.Length - 1; i >= 0; i--) // terminate components in reverse order
{
var component = _components[i];
var componentType = component.GetType();
using (_proflog.DebugDuration<BootLoader>($"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
{
component.Terminate();
}
}
}
}
}
}
@@ -0,0 +1,37 @@
using LightInject;
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents a composition.
/// </summary>
/// <remarks>Although a composition exposes the application's service container, people should use the
/// extension methods (such as <c>PropertyEditors()</c> or <c>SetPublishedContentModelFactory()</c>) and
/// avoid accessing the container. This is because everything needs to be properly registered and with
/// the proper lifecycle. These methods will take care of it. Directly registering into the container
/// may cause issues.</remarks>
public class Composition
{
/// <summary>
/// Initializes a new instance of the <see cref="Composition"/> class.
/// </summary>
/// <param name="container">A container.</param>
/// <param name="level">The runtime level.</param>
public Composition(IServiceContainer container, RuntimeLevel level)
{
Container = container;
RuntimeLevel = level;
}
/// <summary>
/// Gets the container.
/// </summary>
/// <remarks>Use with care!</remarks>
public IServiceContainer Container { get; }
/// <summary>
/// Gets the runtime level.
/// </summary>
public RuntimeLevel RuntimeLevel { get; }
}
}
@@ -1,63 +1,24 @@
using System;
using System.Runtime.CompilerServices;
using LightInject;
using Umbraco.Core.Cache;
using Umbraco.Core.Composing;
using Umbraco.Core.Dictionary;
using Umbraco.Core.IO;
using Umbraco.Core.Logging.Viewer;
using Umbraco.Core.Composing;
using Umbraco.Core.Migrations;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.PackageActions;
using Umbraco.Core.Persistence.Mappers;
using Umbraco.Core.PropertyEditors;
using Umbraco.Core.Strings;
using Umbraco.Core.Sync;
using Umbraco.Core._Legacy.PackageActions;
namespace Umbraco.Core
namespace Umbraco.Core.Components
{
/// <summary>
/// Provides extension methods to the <see cref="Composition"/> class.
/// </summary>
public static partial class CompositionExtensions
public static class CompositionExtensions
{
#region FileSystems
/// <summary>
/// Registers a filesystem.
/// </summary>
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
/// <typeparam name="TImplementing">The implementing type.</typeparam>
/// <param name="composition">The composition.</param>
/// <returns>The register.</returns>
public static void RegisterFileSystem<TFileSystem, TImplementing>(this Composition composition)
where TImplementing : FileSystemWrapper, TFileSystem
where TFileSystem : class
{
composition.RegisterUnique<TFileSystem>(factory =>
{
var fileSystems = factory.GetInstance<FileSystems>();
var supporting = factory.GetInstance<SupportingFileSystems>();
return fileSystems.GetFileSystem<TImplementing>(supporting.For<TFileSystem>());
});
}
/// <summary>
/// Registers a filesystem.
/// </summary>
/// <typeparam name="TFileSystem">The type of the filesystem.</typeparam>
/// <param name="composition">The composition.</param>
/// <returns>The register.</returns>
public static void RegisterFileSystem<TFileSystem>(this Composition composition)
where TFileSystem : FileSystemWrapper
{
composition.RegisterUnique(factory =>
{
var fileSystems = factory.GetInstance<FileSystems>();
var supporting = factory.GetInstance<SupportingFileSystems>();
return fileSystems.GetFileSystem<TFileSystem>(supporting.For<TFileSystem>());
});
}
#endregion
#region Collection Builders
/// <summary>
@@ -65,59 +26,60 @@ namespace Umbraco.Core
/// </summary>
/// <param name="composition">The composition.</param>
public static CacheRefresherCollectionBuilder CacheRefreshers(this Composition composition)
=> composition.WithCollectionBuilder<CacheRefresherCollectionBuilder>();
=> composition.Container.GetInstance<CacheRefresherCollectionBuilder>();
/// <summary>
/// Gets the mappers collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
public static MapperCollectionBuilder Mappers(this Composition composition)
=> composition.WithCollectionBuilder<MapperCollectionBuilder>();
=> composition.Container.GetInstance<MapperCollectionBuilder>();
/// <summary>
/// Gets the package actions collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
internal static PackageActionCollectionBuilder PackageActions(this Composition composition)
=> composition.WithCollectionBuilder<PackageActionCollectionBuilder>();
=> composition.Container.GetInstance<PackageActionCollectionBuilder>();
/// <summary>
/// Gets the data editor collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
public static DataEditorCollectionBuilder DataEditors(this Composition composition)
=> composition.WithCollectionBuilder<DataEditorCollectionBuilder>();
=> composition.Container.GetInstance<DataEditorCollectionBuilder>();
/// <summary>
/// Gets the property value converters collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
public static PropertyValueConverterCollectionBuilder PropertyValueConverters(this Composition composition)
=> composition.WithCollectionBuilder<PropertyValueConverterCollectionBuilder>();
=> composition.Container.GetInstance<PropertyValueConverterCollectionBuilder>();
/// <summary>
/// Gets the url segment providers collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
public static UrlSegmentProviderCollectionBuilder UrlSegmentProviders(this Composition composition)
=> composition.WithCollectionBuilder<UrlSegmentProviderCollectionBuilder>();
=> composition.Container.GetInstance<UrlSegmentProviderCollectionBuilder>();
/// <summary>
/// Gets the validators collection builder.
/// </summary>
/// <param name="composition">The composition.</param>
internal static ManifestValueValidatorCollectionBuilder Validators(this Composition composition)
=> composition.WithCollectionBuilder<ManifestValueValidatorCollectionBuilder>();
=> composition.Container.GetInstance<ManifestValueValidatorCollectionBuilder>();
/// <summary>
/// Gets the components collection builder.
/// Gets the post-migrations collection builder.
/// </summary>
public static ComponentCollectionBuilder Components(this Composition composition)
=> composition.WithCollectionBuilder<ComponentCollectionBuilder>();
/// <param name="composition">The composition.</param>
internal static PostMigrationCollectionBuilder PostMigrations(this Composition composition)
=> composition.Container.GetInstance<PostMigrationCollectionBuilder>();
#endregion
#region Uniques
#region Singleton
/// <summary>
/// Sets the culture dictionary factory.
@@ -127,7 +89,7 @@ namespace Umbraco.Core
public static void SetCultureDictionaryFactory<T>(this Composition composition)
where T : ICultureDictionaryFactory
{
composition.RegisterUnique<ICultureDictionaryFactory, T>();
composition.Container.RegisterSingleton<ICultureDictionaryFactory, T>();
}
/// <summary>
@@ -135,9 +97,9 @@ namespace Umbraco.Core
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating a culture dictionary factory.</param>
public static void SetCultureDictionaryFactory(this Composition composition, Func<IFactory, ICultureDictionaryFactory> factory)
public static void SetCultureDictionaryFactory(this Composition composition, Func<IServiceFactory, ICultureDictionaryFactory> factory)
{
composition.RegisterUnique(factory);
composition.Container.RegisterSingleton(factory);
}
/// <summary>
@@ -147,7 +109,7 @@ namespace Umbraco.Core
/// <param name="factory">A factory.</param>
public static void SetCultureDictionaryFactory(this Composition composition, ICultureDictionaryFactory factory)
{
composition.RegisterUnique(_ => factory);
composition.Container.RegisterSingleton(_ => factory);
}
/// <summary>
@@ -158,7 +120,7 @@ namespace Umbraco.Core
public static void SetPublishedContentModelFactory<T>(this Composition composition)
where T : IPublishedModelFactory
{
composition.RegisterUnique<IPublishedModelFactory, T>();
composition.Container.RegisterSingleton<IPublishedModelFactory, T>();
}
/// <summary>
@@ -166,9 +128,9 @@ namespace Umbraco.Core
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating a published content model factory.</param>
public static void SetPublishedContentModelFactory(this Composition composition, Func<IFactory, IPublishedModelFactory> factory)
public static void SetPublishedContentModelFactory(this Composition composition, Func<IServiceFactory, IPublishedModelFactory> factory)
{
composition.RegisterUnique(factory);
composition.Container.RegisterSingleton(factory);
}
/// <summary>
@@ -178,7 +140,7 @@ namespace Umbraco.Core
/// <param name="factory">A published content model factory.</param>
public static void SetPublishedContentModelFactory(this Composition composition, IPublishedModelFactory factory)
{
composition.RegisterUnique(_ => factory);
composition.Container.RegisterSingleton(_ => factory);
}
/// <summary>
@@ -189,17 +151,17 @@ namespace Umbraco.Core
public static void SetServerRegistrar<T>(this Composition composition)
where T : IServerRegistrar
{
composition.RegisterUnique<IServerRegistrar, T>();
composition.Container.RegisterSingleton<IServerRegistrar, T>();
}
/// <summary>
/// Sets the server registrar.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating a server registrar.</param>
public static void SetServerRegistrar(this Composition composition, Func<IFactory, IServerRegistrar> factory)
/// <param name="factory">A function creating a server registar.</param>
public static void SetServerRegistrar(this Composition composition, Func<IServiceFactory, IServerRegistrar> factory)
{
composition.RegisterUnique(factory);
composition.Container.RegisterSingleton(factory);
}
/// <summary>
@@ -209,7 +171,7 @@ namespace Umbraco.Core
/// <param name="registrar">A server registrar.</param>
public static void SetServerRegistrar(this Composition composition, IServerRegistrar registrar)
{
composition.RegisterUnique(_ => registrar);
composition.Container.RegisterSingleton(_ => registrar);
}
/// <summary>
@@ -220,7 +182,7 @@ namespace Umbraco.Core
public static void SetServerMessenger<T>(this Composition composition)
where T : IServerMessenger
{
composition.RegisterUnique<IServerMessenger, T>();
composition.Container.RegisterSingleton<IServerMessenger, T>();
}
/// <summary>
@@ -228,9 +190,9 @@ namespace Umbraco.Core
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating a server messenger.</param>
public static void SetServerMessenger(this Composition composition, Func<IFactory, IServerMessenger> factory)
public static void SetServerMessenger(this Composition composition, Func<IServiceFactory, IServerMessenger> factory)
{
composition.RegisterUnique(factory);
composition.Container.RegisterSingleton(factory);
}
/// <summary>
@@ -240,7 +202,7 @@ namespace Umbraco.Core
/// <param name="registrar">A server messenger.</param>
public static void SetServerMessenger(this Composition composition, IServerMessenger registrar)
{
composition.RegisterUnique(_ => registrar);
composition.Container.RegisterSingleton(_ => registrar);
}
/// <summary>
@@ -251,7 +213,7 @@ namespace Umbraco.Core
public static void SetShortStringHelper<T>(this Composition composition)
where T : IShortStringHelper
{
composition.RegisterUnique<IShortStringHelper, T>();
composition.Container.RegisterSingleton<IShortStringHelper, T>();
}
/// <summary>
@@ -259,9 +221,9 @@ namespace Umbraco.Core
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating a short string helper.</param>
public static void SetShortStringHelper(this Composition composition, Func<IFactory, IShortStringHelper> factory)
public static void SetShortStringHelper(this Composition composition, Func<IServiceFactory, IShortStringHelper> factory)
{
composition.RegisterUnique(factory);
composition.Container.RegisterSingleton(factory);
}
/// <summary>
@@ -271,54 +233,7 @@ namespace Umbraco.Core
/// <param name="helper">A short string helper.</param>
public static void SetShortStringHelper(this Composition composition, IShortStringHelper helper)
{
composition.RegisterUnique(_ => helper);
}
/// <summary>
/// Sets the underlying media filesystem.
/// </summary>
/// <param name="composition">A composition.</param>
/// <param name="filesystemFactory">A filesystem factory.</param>
public static void SetMediaFileSystem(this Composition composition, Func<IFactory, IFileSystem> filesystemFactory)
=> composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(filesystemFactory);
/// <summary>
/// Sets the underlying media filesystem.
/// </summary>
/// <param name="composition">A composition.</param>
/// <param name="filesystemFactory">A filesystem factory.</param>
public static void SetMediaFileSystem(this Composition composition, Func<IFileSystem> filesystemFactory)
=> composition.RegisterUniqueFor<IFileSystem, IMediaFileSystem>(_ => filesystemFactory());
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <typeparam name="T">The type of the log viewer.</typeparam>
/// <param name="composition">The composition.</param>
public static void SetLogViewer<T>(this Composition composition)
where T : ILogViewer
{
composition.RegisterUnique<ILogViewer, T>();
}
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating a log viewer.</param>
public static void SetLogViewer(this Composition composition, Func<IFactory, ILogViewer> factory)
{
composition.RegisterUnique(factory);
}
/// <summary>
/// Sets the log viewer.
/// </summary>
/// <param name="composition">A composition.</param>
/// <param name="helper">A log viewer.</param>
public static void SetLogViewer(this Composition composition, ILogViewer viewer)
{
composition.RegisterUnique(_ => viewer);
composition.Container.RegisterSingleton(_ => helper);
}
#endregion
@@ -0,0 +1,38 @@
using System;
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a component should be disabled.
/// </summary>
/// <remarks>
/// <para>If a type is specified, disables the component of that type, else disables the component marked with the attribute.</para>
/// <para>This attribute is *not* inherited.</para>
/// <para>This attribute applies to classes only, it is not possible to enable/disable interfaces.</para>
/// <para>If a component ends up being both enabled and disabled: attributes marking the component itself have lower priority
/// than attributes on *other* components, eg if a component declares itself as disabled it is possible to enable it from
/// another component. Anything else is unspecified.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class DisableComponentAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="DisableComponentAttribute"/> class.
/// </summary>
public DisableComponentAttribute()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="DisableComponentAttribute"/> class.
/// </summary>
public DisableComponentAttribute(Type disabledType)
{
DisabledType = disabledType;
}
/// <summary>
/// Gets the disabled type, or null if it is the component marked with the attribute.
/// </summary>
public Type DisabledType { get; }
}
}
@@ -0,0 +1,38 @@
using System;
namespace Umbraco.Core.Components
{
/// <summary>
/// Indicates that a component should be enabled.
/// </summary>
/// <remarks>
/// <para>If a type is specified, enables the component of that type, else enables the component marked with the attribute.</para>
/// <para>This attribute is *not* inherited.</para>
/// <para>This attribute applies to classes only, it is not possible to enable/disable interfaces.</para>
/// <para>If a component ends up being both enabled and disabled: attributes marking the component itself have lower priority
/// than attributes on *other* components, eg if a component declares itself as disabled it is possible to enable it from
/// another component. Anything else is unspecified.</para>
/// </remarks>
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
public class EnableComponentAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="EnableComponentAttribute"/> class.
/// </summary>
public EnableComponentAttribute()
{ }
/// <summary>
/// Initializes a new instance of the <see cref="EnableComponentAttribute"/> class.
/// </summary>
public EnableComponentAttribute(Type enabledType)
{
EnabledType = enabledType;
}
/// <summary>
/// Gets the enabled type, or null if it is the component marked with the attribute.
/// </summary>
public Type EnabledType { get; }
}
}
@@ -0,0 +1,5 @@
namespace Umbraco.Core.Components
{
public interface IRuntimeComponent : IUmbracoComponent
{ }
}
@@ -0,0 +1,21 @@
using Umbraco.Core.Composing;
namespace Umbraco.Core.Components
{
/// <summary>
/// Represents an Umbraco component.
/// </summary>
public interface IUmbracoComponent : IDiscoverable
{
/// <summary>
/// Composes the component.
/// </summary>
/// <param name="composition">The composition.</param>
void Compose(Composition composition);
/// <summary>
/// Terminates the component.
/// </summary>
void Terminate();
}
}
@@ -0,0 +1,6 @@
namespace Umbraco.Core.Components
{
[RequireComponent(typeof(IRuntimeComponent))]
public interface IUmbracoCoreComponent : IUmbracoComponent
{ }
}
@@ -0,0 +1,6 @@
namespace Umbraco.Core.Components
{
[RequireComponent(typeof(UmbracoCoreComponent))]
public interface IUmbracoUserComponent : IUmbracoComponent
{ }
}

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