Merge branch 'v8/dev' into v8/api-docs-update
# Conflicts: # build/build.ps1
This commit is contained in:
+10
-6
@@ -13,7 +13,7 @@
|
||||
*.png binary
|
||||
*.gif binary
|
||||
|
||||
*.cs text=auto diff=csharp
|
||||
*.cs text=auto diff=csharp
|
||||
*.vb text=auto
|
||||
*.c text=auto
|
||||
*.cpp text=auto
|
||||
@@ -41,9 +41,13 @@
|
||||
*.fs text=auto
|
||||
*.fsx text=auto
|
||||
*.hs text=auto
|
||||
*.json text=auto
|
||||
*.xml text=auto
|
||||
|
||||
*.csproj text=auto merge=union
|
||||
*.vbproj text=auto merge=union
|
||||
*.fsproj text=auto merge=union
|
||||
*.dbproj text=auto merge=union
|
||||
*.sln text=auto eol=crlf merge=union
|
||||
*.csproj text=auto merge=union
|
||||
*.vbproj text=auto merge=union
|
||||
*.fsproj text=auto merge=union
|
||||
*.dbproj text=auto merge=union
|
||||
*.sln text=auto eol=crlf merge=union
|
||||
|
||||
*.gitattributes text=auto
|
||||
|
||||
+7
-3
@@ -1,4 +1,4 @@
|
||||
# Umbraco Cms Build
|
||||
# Umbraco CMS Build
|
||||
|
||||
## Are you sure?
|
||||
|
||||
@@ -39,8 +39,12 @@ To build Umbraco, fire up PowerShell and move to Umbraco's repository root (the
|
||||
|
||||
build/build.ps1
|
||||
|
||||
If you only see a build.bat-file, you're probably on the wrong branch. If you switch to the correct branch (dev-v8) the file will appear and you can build it.
|
||||
|
||||
You might run into [Powershell quirks](#powershell-quirks).
|
||||
|
||||
If it runs without errors; Hooray! Now you can continue with [the next step](CONTRIBUTING.md#how-do-i-begin) and open the solution and build it.
|
||||
|
||||
### Build Infrastructure
|
||||
|
||||
The Umbraco Build infrastructure relies on a PowerShell object. The object can be retrieved with:
|
||||
@@ -66,7 +70,7 @@ The Visual Studio object is `null` when Visual Studio has not been detected (eg
|
||||
* `Path`: Visual Studio installation path (eg some place under `Program Files`)
|
||||
* `Major`: Visual Studio major version (eg `15` for VS 2017)
|
||||
* `Minor`: Visual Studio minor version
|
||||
* `MsBUild`: the absolute path to the MsBuild executable
|
||||
* `MsBuild`: the absolute path to the MsBuild executable
|
||||
|
||||
#### GetUmbracoVersion
|
||||
|
||||
@@ -209,4 +213,4 @@ The best solution is to unblock the Zip file before un-zipping: right-click the
|
||||
|
||||
### Git Quirks
|
||||
|
||||
Git might have issues dealing with long file paths during build. You may want/need to enable `core.longpaths` support (see [this page](https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path) for details).
|
||||
Git might have issues dealing with long file paths during build. You may want/need to enable `core.longpaths` support (see [this page](https://github.com/msysgit/msysgit/wiki/Git-cannot-create-a-file-or-directory-with-a-long-path) for details).
|
||||
|
||||
@@ -29,4 +29,4 @@ Don't rest on your laurels and never accept the status quo. Contribute and give
|
||||
|
||||
## Friendly
|
||||
|
||||
Don’t judge upon mistakes made but rather upon the speed and quality with which mistakes are corrected. Friendly posts and contributions generate smiles and builds long lasting relationships.
|
||||
Don’t judge upon mistakes made but rather upon the speed and quality with which mistakes are corrected. Friendly posts and contributions generate smiles and build long lasting relationships.
|
||||
+51
-40
@@ -2,15 +2,15 @@
|
||||
|
||||
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
|
||||
|
||||
The following is a set of guidelines for contributing to Umbraco CMS.
|
||||
The following is a set of guidelines, for contributing to Umbraco CMS.
|
||||
|
||||
These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a pull request.
|
||||
These are mostly guidelines, not rules. Use your best judgement, and feel free to propose changes to this document in a pull request.
|
||||
|
||||
Remember, we're a friendly bunch and are happy with whatever contribution you might provide. Below are guidelines for success that we've gathered over the years. If you choose to ignore them then we still love you 💖.
|
||||
|
||||
**Code of conduct**
|
||||
|
||||
This project and everyone participating in it is governed by the [our Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [Sebastiaan Janssen - sj@umbraco.dk](mailto:sj@umbraco.dk).
|
||||
This project and everyone participating in it, is governed by the [our Code of Conduct](CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please report unacceptable behavior to [Sebastiaan Janssen - sj@umbraco.dk](mailto:sj@umbraco.dk).
|
||||
|
||||
**Table of contents**
|
||||
|
||||
@@ -22,13 +22,13 @@ This project and everyone participating in it is governed by the [our Code of Co
|
||||
|
||||
[Reviews](#reviews)
|
||||
* [Styleguides](#styleguides)
|
||||
* [The PR team](#the-pr-team)
|
||||
* [The Core Contributors](#the-core-contributors-team)
|
||||
* [Questions?](#questions)
|
||||
|
||||
[Working with the code](#working-with-the-code)
|
||||
* [Building Umbraco from source code](#building-umbraco-from-source-code)
|
||||
* [Working with the source code](#working-with-the-source-code)
|
||||
* [Making changes after the PR was opened](#making-changes-after-the-pr-was-opened)
|
||||
* [Making changes after the PR is open](#making-changes-after-the-pr-is-open)
|
||||
* [Which branch should I target for my contributions?](#which-branch-should-i-target-for-my-contributions)
|
||||
* [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository)
|
||||
|
||||
@@ -38,11 +38,11 @@ This document gives you a quick overview on how to get started.
|
||||
|
||||
### 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 occasion 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.
|
||||
|
||||
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, so we can ensure that you don't put all your hard work into something we would not be able to merge.
|
||||
|
||||
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.
|
||||
Remember, it is always worth working on an issue from the `Up for grabs` list or even asking for some feedback before you send us a PR. This way, your PR will not be closed as unwanted.
|
||||
|
||||
### What can I start with?
|
||||
|
||||
@@ -59,37 +59,38 @@ Great question! The short version goes like this:
|
||||
* **Clone** - when GitHub has created your fork, you can clone it in your favorite Git tool
|
||||
|
||||

|
||||
|
||||
|
||||
* **Switch to the correct branch** - switch to the `v8/contrib` branch
|
||||
* **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! 🎉 **Important:** 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`. When you have a branch, commit your changes. Don't commit to `v8/dev`, create a new branch first.
|
||||
* **Commit** - done? Yay! 🎉 **Important:** 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`. When you have a branch, commit your changes. Don't commit to `v8/contrib`, create a new branch first.
|
||||
* **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.
|
||||
* **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress - you can now make use of GitHub's draft pull request status, detailed [here](https://github.blog/2019-02-14-introducing-draft-pull-requests/)). 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.
|
||||
|
||||

|
||||
|
||||
### Pull requests
|
||||
The most successful pull requests usually look a like this:
|
||||
|
||||
* Fill in the required template
|
||||
* Fill in the required template (shown when starting a PR on GitHub), and link your pull request to an issue on the [issue tracker,](https://github.com/umbraco/Umbraco-CMS/issues) if applicable.
|
||||
* 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
|
||||
* 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.
|
||||
|
||||
Again, these are guidelines, not strict requirements.
|
||||
Again, these are guidelines, not strict requirements. However, the more information that you give to us, the more we have to work with when considering your contributions. Good documentation of a pull request can really speed up the time it takes to review and merge your work!
|
||||
|
||||
## Reviews
|
||||
|
||||
You've sent us your first contribution, congratulations! Now what?
|
||||
You've sent us your first contribution - congratulations! Now what?
|
||||
|
||||
The [pull request team](#the-pr-team) can now start reviewing your proposed changes and give you feedback on them. If it's not perfect, we'll either fix up what we need or we can request you to make some additional changes.
|
||||
The [pull request team](#the-pr-team) can now start reviewing your proposed changes and give you feedback on them. If it's not perfect, we'll either fix up what we need or we can request that you make some additional changes.
|
||||
|
||||
We have [a process in place which you can read all about](REVIEW_PROCESS.md). The very abbreviated version is:
|
||||
|
||||
- Your PR will get a reply within 48 hours
|
||||
- An in-depth reply will be added within at most 2 weeks
|
||||
- The PR will be either merged or rejected within at most 4 weeks
|
||||
- Sometimes it is difficult to meet these timelines and we'll talk to you
|
||||
- Sometimes it is difficult to meet these timelines and we'll talk to you if this is the case.
|
||||
|
||||
### Styleguides
|
||||
|
||||
@@ -97,23 +98,24 @@ To be honest, we don't like rules very much. We trust you have the best of inten
|
||||
|
||||
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.
|
||||
|
||||
### The PR team
|
||||
### The Core Contributors team
|
||||
|
||||
The pull request team consists of a member of Umbraco HQ, [Sebastiaan](https://github.com/nul800sebastiaan), who gets assistance from the following community members
|
||||
The Core Contributors team consists of one member of Umbraco HQ, [Sebastiaan](https://github.com/nul800sebastiaan), who gets assistance from the following community members who have comitted to volunteering their free time:
|
||||
|
||||
- [Anders Bjerner](https://github.com/abjerner)
|
||||
- [Dave Woestenborghs](https://github.com/dawoe)
|
||||
- [Emma Burstow](https://github.com/emmaburstow)
|
||||
- [Poornima Nayar](https://github.com/poornimanayar)
|
||||
- [Kenn Jacobsen](https://twitter.com/KennJacobsen_DK)
|
||||
|
||||
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.
|
||||
|
||||
These wonderful people aim to provide you with a first reply to your PR, review and test out your changes and on occasions, they might ask more questions. If they are happy with your work, they'll let Umbraco HQ know by approving the PR. Hq will have final sign-off and will check the work again before it is merged.
|
||||
|
||||
### Questions?
|
||||
|
||||
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:
|
||||
You can get in touch with [the core contributors team](#the-core-contributors-team) in multiple ways; we love open conversations and we are a friendly bunch. No question you have is stupid. Any question 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
|
||||
- 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, so one of us will be on hand and ready to point you in the right direction.
|
||||
|
||||
## Working with the code
|
||||
|
||||
@@ -121,23 +123,24 @@ You can get in touch with [the PR team](#the-pr-team) in multiple ways, we love
|
||||
|
||||
In order to build the Umbraco source code locally, first make sure you have the following installed.
|
||||
|
||||
* Visual Studio 2017 v15.9.7+
|
||||
* Node v10+
|
||||
* npm v6.4.1+
|
||||
* [Visual Studio 2017 v15.9.7+](https://visualstudio.microsoft.com/vs/)
|
||||
* [Node.js v10+](https://nodejs.org/en/download/)
|
||||
* npm v6.4.1+ (installed with Node.js)
|
||||
* [Git command line](https://git-scm.com/download/)
|
||||
|
||||
The easiest way to get started is to run `build.ps1` 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.
|
||||
The easiest way to get started is to open `src\umbraco.sln` in Visual Studio 2017 (version 15.9.7 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 (version 15.9.7 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 run `build.ps1` from the Powershell command line, 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.
|
||||
|
||||

|
||||
|
||||
After this build completes, you should be able to hit `F5` in Visual Studio to build and run the project. A IISExpress webserver will start and the Umbraco installer will pop up in your browser, follow the directions there to get a working Umbraco install up and running.
|
||||
After this build completes, you should be able to hit `F5` in Visual Studio to build and run the project. A IISExpress webserver will start and the Umbraco installer will pop up in your browser. Follow the directions there to get a working Umbraco install up and running.
|
||||
|
||||
### Working with the source code
|
||||
|
||||
Some parts of our source code are over 10 years old now. And when we say "old", we mean "mature" of course!
|
||||
|
||||
There's two big areas that you should know about:
|
||||
There are two big areas that you should know about:
|
||||
|
||||
1. The Umbraco backoffice is a extensible AngularJS app and requires you to run a `gulp dev` command while you're working with it, so changes are copied over to the appropriate directories and you can refresh your browser to view the results of your changes.
|
||||
You may need to run the following commands to set up gulp properly:
|
||||
@@ -146,30 +149,34 @@ There's two big areas that you should know about:
|
||||
npm install
|
||||
npm run build
|
||||
```
|
||||
The caching for the back office has been described as 'aggressive' so we often find it's best when making back office changes to disable caching in the browser to help you to see the changes you're making.
|
||||
|
||||
2. "The rest" is a C# based codebase, which is mostly ASP.NET MVC based. You can make changes, build them in Visual Studio, and hit `F5` to see the result.
|
||||
|
||||
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.
|
||||
To find the general areas for 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 C# application](https://our.umbraco.com/apidocs/csharp/)
|
||||
|
||||
### Which branch should I target for my contributions?
|
||||
|
||||
We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-flow/), don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to something, usually `v8/dev`. Whatever the default is, that's where we'd like you to target your contributions.
|
||||
We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-flow/), but don't worry if you are not familiar with it. The most important thing you need to know is that when you fork the Umbraco repository, the default branch is set to something, usually `v8/contrib`. If you are working on v8, this is the branch you should be targetting. For v7 contributions, please target 'v7/dev'.
|
||||
|
||||
Please note: we are no longer accepting features for v7 but will continue to merge bug fixes as and when they arise.
|
||||
|
||||

|
||||
|
||||
### Making changes after the PR was opened
|
||||
### Making changes after the PR is open
|
||||
|
||||
If you make the corrections we ask for in the same branch and push them to your fork again, the pull request automatically updates with the additional commit(s) so we can review it again. If all is well, we'll merge the code and your commits are forever part of Umbraco!
|
||||
|
||||
### Keeping your Umbraco fork in sync with the main repository
|
||||
|
||||
We recommend you sync with our repository before you submit your pull request. That way, you can fix any potential merge conflicts and make our lives a little bit easier.
|
||||
We recommend you to sync with our repository before you submit your pull request. That way, you can fix any potential merge conflicts and make our lives a little bit easier.
|
||||
|
||||
Also, if you've submitted a pull request three weeks ago and want to work on something new, you'll want to get the latest code to build against of course.
|
||||
Also, if you have submitted a pull request three weeks ago and want to work on something new, you'll want to get the latest code to build against of course.
|
||||
|
||||
To sync your fork with this original one, you'll have to add the upstream url, you only have to do this once:
|
||||
To sync your fork with this original one, you'll have to add the upstream url. You only have to do this once:
|
||||
|
||||
```
|
||||
git remote add upstream https://github.com/umbraco/Umbraco-CMS.git
|
||||
@@ -179,9 +186,13 @@ Then when you want to get the changes from the main repository:
|
||||
|
||||
```
|
||||
git fetch upstream
|
||||
git rebase upstream/v8/dev
|
||||
git rebase upstream/v8/contrib
|
||||
```
|
||||
|
||||
In this command we're syncing with the `v8/dev` branch, but you can of course choose another one if needed.
|
||||
In this command we're syncing with the `v8/contrib` 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))
|
||||
|
||||
### And finally
|
||||
|
||||
We welcome all kinds of contributions to this repository. If you don't feel you'd like to make code changes here, you can visit our [documentation repository](https://github.com/umbraco/UmbracoDocs) and use your experience to contribute to making the docs we have, even better. We also encourage community members to feel free to comment on others' pull requests and issues - the expertise we have is not limited to the Core Contributors and HQ. So, if you see something on the issue tracker or pull requests you feel you can add to, please don't be shy.
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
# Contributing to Umbraco CMS
|
||||
|
||||
When you’re considering creating a pull request for Umbraco CMS, we will categorize them in two different sizes, small and large.
|
||||
|
||||
The process for both sizes is very similar, as [explained in the contribution document](CONTRIBUTING.md#how-do-i-begin).
|
||||
|
||||
## Small PRs
|
||||
Bug fixes and small improvements - can be recognized by seeing a small number of changes and possibly a small number of new files.
|
||||
|
||||
We’re usually able to handle small PRs pretty quickly. A community volunteer will do the initial review and flag it for Umbraco HQ as “community tested”. If everything looks good, it will be merged pretty quickly [as per the described process](REVIEW_PROCESS.md).
|
||||
|
||||
### Up for grabs
|
||||
|
||||
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. 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.
|
||||
|
||||
## 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 (NuGet’s packages.config, NPM’s packages.json, etc.).
|
||||
|
||||
We would love to follow the same process for larger PRs but this is not always possible due to time limitations and priorities that need to be aligned. We don’t want to put up any barriers, but this document should set the correct expectations.
|
||||
|
||||
Please make sure to describe your idea in an issue, it helps to put in mockup screenshots or videos.
|
||||
|
||||
If the change makes sense for HQ to include in Umbraco CMS we will leave you some feedback on how we’d like to see it being implemented.
|
||||
|
||||
If a larger pull request is encouraged by Umbraco HQ, the process will be similar to what is described in the [small PRs process](#small-prs) above, we strive to feedback within 14 days. Finalizing and merging the PR might take longer though as it will likely need to be picked up by the development team to make sure everything is in order. We’ll keep you posted on the progress.
|
||||
|
||||
It is highly recommended that you speak to the HQ before making large, complex changes.
|
||||
|
||||
### Pull request or package?
|
||||
|
||||
If it doesn’t 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 fix bugs.
|
||||
|
||||
Eventually, a package could "graduate" to be included in the CMS.
|
||||
@@ -7,15 +7,15 @@ 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
|
||||
Please fill 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
|
||||
|
||||
-->
|
||||
|
||||
## Umbraco version
|
||||
|
||||
I am seeing this issue on Umbraco version: <!-- please note the version here -->
|
||||
|
||||
|
||||
Reproduction
|
||||
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
# [Umbraco CMS](https://umbraco.com) · [](../LICENSE.md) [](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [](CONTRIBUTING.md) [](https://pullreminders.com?ref=badge)
|
||||
# [Umbraco CMS](https://umbraco.com) · [](../LICENSE.md) [](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [](CONTRIBUTING.md) [](https://twitter.com/intent/follow?screen_name=umbraco)
|
||||
|
||||
Umbraco is the friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 500,000 websites worldwide. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
|
||||
|
||||
@@ -21,7 +21,7 @@ Please also see our [Code of Conduct](CODE_OF_CONDUCT.md).
|
||||
|
||||
[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.
|
||||
If you want to DIY, then 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.
|
||||
|
||||
## Documentation
|
||||
|
||||
@@ -29,7 +29,7 @@ The documentation for Umbraco CMS can be found [on Our Umbraco](https://our.umbr
|
||||
|
||||
## Join the Umbraco 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.
|
||||
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.
|
||||
|
||||
Besides "Our", we all support each other also via Twitter: [Umbraco HQ](https://twitter.com/umbraco), [Release Updates](https://twitter.com/umbracoproject), [#umbraco](https://twitter.com/hashtag/umbraco)
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
# Review process
|
||||
|
||||
You're an awesome person and have sent us your contribution in the form of a pull request! It's now time to relax for a bit and wait for our response.
|
||||
|
||||
In order to set some expectations, here's what happens next.
|
||||
|
||||
## Review process
|
||||
|
||||
You will get an initial reply within 48 hours (workdays) to acknowledge that we’ve seen your PR and we’ll pick it up as soon as we can.
|
||||
|
||||
You will get feedback within at most 14 days after opening the PR. You’ll most likely get feedback sooner though. Then there are a few possible outcomes:
|
||||
|
||||
- Your proposed change is awesome! We merge it in and it will be included in the next minor release of Umbraco
|
||||
- If the change is a high priority bug fix, we will cherry-pick it into the next patch release as well so that we can release it as soon as possible
|
||||
- Your proposed change is awesome but needs a bit more work, we’ll give you feedback on the changes we’d like to see
|
||||
- Your proposed change is awesome but.. not something we’re looking to include at this point. We’ll close your PR and the related issue (we’ll be nice about it!)
|
||||
|
||||
## Are you still available?
|
||||
|
||||
We understand you have other things to do and can't just drop everything to help us out.
|
||||
So if we’re asking for your help to improve the PR we’ll wait for two weeks to give you a fair chance to make changes. We’ll ask for an update if we don’t hear back from you after that time.
|
||||
|
||||
If we don’t hear back from you for 4 weeks, we’ll close the PR so that it doesn’t just hang around forever. You’re very welcome to re-open it once you have some more time to spend on it.
|
||||
|
||||
There will be times that we really like your proposed changes and we’ll finish the final improvements we’d like to see ourselves. You still get the credits and your commits will live on in the git repository.
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 24 KiB |
+6
-1
@@ -55,6 +55,7 @@ App_Data/TEMP/*
|
||||
src/Umbraco.Web.UI/[Cc]ss/*
|
||||
src/Umbraco.Web.UI/App_Code/*
|
||||
src/Umbraco.Web.UI/App_Data/*
|
||||
src/Umbraco.Web.UI/data/*
|
||||
src/Umbraco.Tests/App_Data/*
|
||||
src/Umbraco.Web.UI/[Mm]edia/*
|
||||
src/Umbraco.Web.UI/[Mm]aster[Pp]ages/*
|
||||
@@ -99,6 +100,9 @@ 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/[Jj]s/navigation.controller.js
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/main.controller.js
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Jj]s/utilities.js
|
||||
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/
|
||||
src/Umbraco.Web.UI/[Uu]mbraco/[Vv]iews/**/*.js
|
||||
@@ -132,7 +136,8 @@ src/Umbraco.Web.UI.Client/bower_components/*
|
||||
preserve.belle
|
||||
|
||||
#Ignore Rule for output of generated documentation files from Grunt docserve
|
||||
src/Umbraco.Web.UI.Client/docs/api
|
||||
src/Umbraco.Web.UI.Docs/api
|
||||
src/Umbraco.Web.UI.Docs/package-lock.json
|
||||
src/*.boltdata/
|
||||
src/umbraco.sln.ide/*
|
||||
src/.vs/
|
||||
|
||||
@@ -7,6 +7,5 @@
|
||||
-->
|
||||
<packageSources>
|
||||
<add key="UmbracoCoreMyGet" value="https://www.myget.org/F/umbracocore/api/v3/index.json" />
|
||||
<add key="ExamineAppVeyor" value="https://ci.appveyor.com/nuget/examine-f73l6qv0oqfh/" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<repository type="git" url="https://github.com/umbraco/umbraco-cms" />
|
||||
<dependencies>
|
||||
|
||||
<group targetFramework="net472">
|
||||
@@ -39,10 +40,12 @@
|
||||
<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="Serilog.Sinks.Map" version="[1.0.0,1.999999)" />
|
||||
<dependency id="Serilog.Sinks.Async" version="[1.3.0,1.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>
|
||||
@@ -50,9 +53,9 @@
|
||||
<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.Core.xml" target="lib\net472\Umbraco.Core.xml" />
|
||||
|
||||
<!-- symbols -->
|
||||
<file src="$BuildTmp$\bin\Umbraco.Core.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\Umbraco.Core.pdb" target="lib\net472\Umbraco.Core.pdb" />
|
||||
</files>
|
||||
</package>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<summary>Contains the core assemblies needed to run Umbraco Cms</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<repository type="git" url="https://github.com/umbraco/umbraco-cms" />
|
||||
<dependencies>
|
||||
|
||||
<group targetFramework="net472">
|
||||
@@ -24,10 +25,10 @@
|
||||
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="ClientDependency" version="[1.9.9,1.999999)" />
|
||||
<dependency id="ClientDependency-Mvc5" version="[1.9.3,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="Examine" version="[1.0.2,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)" />
|
||||
@@ -43,7 +44,7 @@
|
||||
<dependency id="System.Threading.Tasks.Dataflow" version="[4.9.0,4.999999)" />
|
||||
|
||||
</group>
|
||||
|
||||
|
||||
</dependencies>
|
||||
</metadata>
|
||||
<files>
|
||||
@@ -51,14 +52,17 @@
|
||||
<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" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Umbraco.ModelsBuilder.Embedded.dll" target="lib\net472\Umbraco.ModelsBuilder.Embedded.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" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.xml" target="lib\net472\Umbraco.Web.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Umbraco.Web.UI.xml" target="lib\net472\Umbraco.Web.UI.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Umbraco.Examine.xml" target="lib\net472\Umbraco.Examine.xml" />
|
||||
<file src="$BuildTmp$\WebApp\bin\Umbraco.ModelsBuilder.Embedded.xml" target="lib\net472\Umbraco.ModelsBuilder.Embedded.xml" />
|
||||
|
||||
<!-- symbols -->
|
||||
<file src="$BuildTmp$\bin\Umbraco.Web.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\Umbraco.Examine.pdb" target="lib" />
|
||||
<file src="$BuildTmp$\bin\Umbraco.Web.pdb" target="lib\net472\Umbraco.Web.pdb" />
|
||||
<file src="$BuildTmp$\bin\Umbraco.Examine.pdb" target="lib\net472\Umbraco.Examine.pdb" />
|
||||
<file src="$BuildTmp$\bin\Umbraco.ModelsBuilder.Embedded.pdb" target="lib\net472\Umbraco.ModelsBuilder.Embedded.pdb" />
|
||||
</files>
|
||||
</package>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<summary>Installs Umbraco Cms in your Visual Studio ASP.NET project</summary>
|
||||
<language>en-US</language>
|
||||
<tags>umbraco</tags>
|
||||
<repository type="git" url="https://github.com/umbraco/umbraco-cms" />
|
||||
<dependencies>
|
||||
|
||||
<group targetFramework="net472">
|
||||
@@ -25,11 +26,9 @@
|
||||
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.4]" />
|
||||
<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)" />
|
||||
|
||||
</group>
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
|
||||
888
|
||||
888
|
||||
888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
|
||||
888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
|
||||
888 888 888 888 888 888 888 888 .d888888 888 888 888
|
||||
Y88b 888 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
|
||||
"Y88888 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
|
||||
888
|
||||
888
|
||||
888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
|
||||
888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
|
||||
888 888 888 888 888 888 888 888 .d888888 888 888 888
|
||||
Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
|
||||
"Y888P" 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
|
||||
|
||||
------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -1,30 +1,26 @@
|
||||
|
||||
_ _ __ __ ____ _____ _____ ____
|
||||
| | | | \/ | _ \| __ \ /\ / ____/ __ \
|
||||
| | | | \ / | |_) | |__) | / \ | | | | | |
|
||||
| | | | |\/| | _ <| _ / / /\ \| | | | | |
|
||||
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
|
||||
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
|
||||
888
|
||||
888
|
||||
888 888 88888b.d88b. 88888b. 888d888 8888b. .d8888b .d88b.
|
||||
888 888 888 "888 "88b 888 "88b 888P" "88b d88P" d88""88b
|
||||
888 888 888 888 888 888 888 888 .d888888 888 888 888
|
||||
Y88 88Y 888 888 888 888 d88P 888 888 888 Y88b. Y88..88P
|
||||
"Y888P" 888 888 888 88888P" 888 "Y888888 "Y8888P "Y88P"
|
||||
|
||||
----------------------------------------------------
|
||||
------------------------------------------------------------------
|
||||
|
||||
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
|
||||
sure to do a compare and merge with the NuGetBackup folder.
|
||||
We've done our best to transform your configuration files but in case something is not quite right: we recommmend you look in source control for the previous version so you can find the original files before they were transformed.
|
||||
|
||||
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.
|
||||
|
||||
Please read the release notes on our.umbraco.com:
|
||||
http://our.umbraco.com/contribute/releases
|
||||
https://our.umbraco.com/contribute/releases
|
||||
|
||||
- Umbraco
|
||||
|
||||
@@ -8,23 +8,23 @@
|
||||
</configSections>
|
||||
|
||||
<system.web.webPages.razor>
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(factoryType)" xdt:Transform="SetAttributes(factoryType)" />
|
||||
<host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" xdt:Locator="Match(factoryType)" xdt:Transform="SetAttributes(factoryType)" />
|
||||
<pages pageBaseType="System.Web.Mvc.WebViewPage">
|
||||
<namespaces>
|
||||
<add namespace="Umbraco.Web.PublishedContentModels" xdt:Transform="InsertIfMissing" />
|
||||
<add namespace="Umbraco.Web.PublishedModels" xdt:Transform="InsertIfMissing" />
|
||||
</namespaces>
|
||||
</pages>
|
||||
</system.web.webPages.razor>
|
||||
|
||||
<system.web>
|
||||
<pages
|
||||
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"
|
||||
xdt:Locator="Match(pageParserFilterType,pageBaseType,userControlBaseType)"
|
||||
xdt:Transform="SetAttributes(pageParserFilterType,pageBaseType,userControlBaseType)">
|
||||
<controls>
|
||||
<add assembly="System.Web.Mvc, Version=5.2.3.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" xdt:Locator="Match(namespace)" xdt:Transform="SetAttributes(assembly)" />
|
||||
<add assembly="System.Web.Mvc, Version=5.2.7.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" xdt:Locator="Match(namespace)" xdt:Transform="SetAttributes(assembly)" />
|
||||
</controls>
|
||||
</pages>
|
||||
</system.web>
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
<add name="umbracoBaseRequestModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<add name="viewstateMoverModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<add name=" UmbracoModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<add name="UmbracoModule" type="Umbraco.Web.UmbracoModule,umbraco" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
|
||||
<add name="UmbracoModule" type="Umbraco.Web.UmbracoModule,Umbraco.Web" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
|
||||
</httpModules>
|
||||
|
||||
<httpHandlers xdt:Transform="InsertIfMissing" />
|
||||
@@ -76,7 +76,7 @@
|
||||
<!-- Note, we're removing the one that starts with a space here, don't correct it -->
|
||||
<!-- This to fix a quirk we for a lot of releases where we added it with the space by default -->
|
||||
<add name=" UmbracoModule" xdt:Locator="Match(name)" xdt:Transform="Remove" />
|
||||
<add name="UmbracoModule" type="Umbraco.Web.UmbracoModule,umbraco" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
|
||||
<add name="UmbracoModule" type="Umbraco.Web.UmbracoModule,Umbraco.Web" xdt:Locator="Match(name)" xdt:Transform="InsertIfMissing" />
|
||||
</modules>
|
||||
|
||||
<staticContent xdt:Transform="InsertIfMissing" />
|
||||
@@ -128,6 +128,7 @@
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Data.SqlServerCe')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.ValueTuple')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Net.Http.Formatting')" xdt:Transform="Remove" />
|
||||
<dependentAssembly xdt:Locator="Condition(./_defaultNamespace:assemblyIdentity/@name='System.Collections.Immutable')" xdt:Transform="Remove" />
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
||||
|
||||
@@ -18,9 +18,20 @@ if ($project) {
|
||||
|
||||
# Copy umbraco and umbraco_files from package to project folder
|
||||
$umbracoFolder = Join-Path $projectPath "Umbraco"
|
||||
New-Item -ItemType Directory -Force -Path $umbracoFolder
|
||||
New-Item -ItemType Directory -Force -Path $umbracoFolder
|
||||
$umbracoFolderSource = Join-Path $installPath "UmbracoFiles\Umbraco"
|
||||
robocopy $umbracoFolderSource $umbracoFolder /is /it /e /xf UI.xml /LOG:$copyLogsPath\UmbracoCopy.log
|
||||
|
||||
Write-Host "copying files to $umbracoFolder ..."
|
||||
# see https://support.microsoft.com/en-us/help/954404/return-codes-that-are-used-by-the-robocopy-utility-in-windows-server-2
|
||||
robocopy $umbracoFolderSource $umbracoFolder /is /it /e
|
||||
if (($lastexitcode -eq 1) -or ($lastexitcode -eq 3) -or ($lastexitcode -eq 5) -or ($lastexitcode -eq 7))
|
||||
{
|
||||
write-host "Copy succeeded!"
|
||||
}
|
||||
else
|
||||
{
|
||||
write-host "Copy failed with exit code:" $lastexitcode
|
||||
}
|
||||
|
||||
$copyWebconfig = $true
|
||||
$destinationWebConfig = Join-Path $projectPath "Web.config"
|
||||
@@ -40,7 +51,11 @@ if ($project) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Catch { }
|
||||
Catch
|
||||
{
|
||||
Write-Host "An error occurred:"
|
||||
Write-Host $_
|
||||
}
|
||||
}
|
||||
|
||||
if($copyWebconfig -eq $true)
|
||||
@@ -74,7 +89,9 @@ if ($project) {
|
||||
}
|
||||
Catch
|
||||
{
|
||||
# Not a big problem if this fails, let it go
|
||||
# Not a big problem if this fails, let it go
|
||||
# Write-Host "An error occurred:"
|
||||
# Write-Host $_
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
# get NuGet
|
||||
$cache = 4
|
||||
$nuget = "$scriptTemp\nuget.exe"
|
||||
# ensure the correct NuGet-source is used. This one is used by Umbraco
|
||||
$nugetsourceUmbraco = "https://www.myget.org/F/umbracocore/api/v3/index.json"
|
||||
if (-not $local)
|
||||
{
|
||||
$source = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
|
||||
@@ -61,7 +63,7 @@
|
||||
# get the build system
|
||||
if (-not $local)
|
||||
{
|
||||
$params = "-OutputDirectory", $scriptTemp, "-Verbosity", "quiet", "-PreRelease"
|
||||
$params = "-OutputDirectory", $scriptTemp, "-Verbosity", "quiet", "-PreRelease", "-Source", $nugetsourceUmbraco
|
||||
&$nuget install Umbraco.Build @params
|
||||
if (-not $?) { throw "Failed to download Umbraco.Build." }
|
||||
}
|
||||
|
||||
+19
-17
@@ -15,7 +15,7 @@
|
||||
[Parameter(Mandatory=$false)]
|
||||
[Alias("doc")]
|
||||
[switch] $docfx = $false,
|
||||
|
||||
|
||||
# keep the build directories, don't clear them
|
||||
[Parameter(Mandatory=$false)]
|
||||
[Alias("c")]
|
||||
@@ -375,11 +375,14 @@
|
||||
|
||||
})
|
||||
|
||||
$nugetsourceUmbraco = "https://api.nuget.org/v3/index.json"
|
||||
|
||||
$ubuild.DefineMethod("RestoreNuGet",
|
||||
{
|
||||
Write-Host "Restore NuGet"
|
||||
Write-Host "Logging to $($this.BuildTemp)\nuget.restore.log"
|
||||
&$this.BuildEnv.NuGet restore "$($this.SolutionRoot)\src\Umbraco.sln" > "$($this.BuildTemp)\nuget.restore.log"
|
||||
$params = "-Source", $nugetsourceUmbraco
|
||||
&$this.BuildEnv.NuGet restore "$($this.SolutionRoot)\src\Umbraco.sln" > "$($this.BuildTemp)\nuget.restore.log" @params
|
||||
if (-not $?) { throw "Failed to restore NuGet packages." }
|
||||
})
|
||||
|
||||
@@ -392,13 +395,13 @@
|
||||
&$this.BuildEnv.NuGet Pack "$nuspecs\UmbracoCms.Core.nuspec" `
|
||||
-Properties BuildTmp="$($this.BuildTemp)" `
|
||||
-Version "$($this.Version.Semver.ToString())" `
|
||||
-Symbols -SymbolPackageFormat snupkg -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmscore.log"
|
||||
-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 -SymbolPackageFormat snupkg -Verbosity detailed -outputDirectory "$($this.BuildOutput)" > "$($this.BuildTemp)\nupack.cmsweb.log"
|
||||
-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" `
|
||||
@@ -429,51 +432,50 @@
|
||||
Write-Host "Prepare Azure Gallery"
|
||||
$this.CopyFile("$($this.SolutionRoot)\build\Azure\azuregalleryrelease.ps1", $this.BuildOutput)
|
||||
})
|
||||
|
||||
|
||||
$ubuild.DefineMethod("BuildCSharpDocs",
|
||||
{
|
||||
Write-Host "Building C# Documentation"
|
||||
|
||||
|
||||
$src = "$($this.SolutionRoot)\src"
|
||||
$tmp = $this.BuildTemp
|
||||
$out = $this.BuildOutput
|
||||
$DocFxJson = Join-Path -Path $src "\ApiDocs\docfx.json"
|
||||
$DocFxSiteOutput = Join-Path -Path $tmp "\_site\*.*"
|
||||
|
||||
|
||||
# run DocFx
|
||||
$DocFx = $this.BuildEnv.DocFx
|
||||
|
||||
|
||||
& $DocFx metadata $DocFxJson
|
||||
& $DocFx build $DocFxJson
|
||||
|
||||
# zip it
|
||||
& $this.BuildEnv.Zip a -tzip -r "$out\csharp-docs.zip" $DocFxSiteOutput
|
||||
})
|
||||
|
||||
|
||||
$ubuild.DefineMethod("BuildAngularDocs",
|
||||
{
|
||||
Write-Host "Building Angular Documentation"
|
||||
|
||||
|
||||
$src = "$($this.SolutionRoot)\src"
|
||||
$out = $this.BuildOutput
|
||||
|
||||
# Check if the solution has been built
|
||||
if (!(Test-Path "$src\Umbraco.Web.UI.Client\node_modules")) {throw "Umbraco needs to be built before generating the Angular Docs"}
|
||||
|
||||
Push-Location "$src\Umbraco.Web.UI.Client"
|
||||
"Moving to Umbraco.Web.UI.Docs folder"
|
||||
cd ..\src\Umbraco.Web.UI.Docs
|
||||
|
||||
"Generating the docs and waiting before executing the next commands"
|
||||
& gulp docs | Out-Null
|
||||
& npm install
|
||||
& npx gulp docs
|
||||
|
||||
Pop-Location
|
||||
|
||||
# change baseUrl
|
||||
$BaseUrl = "https://our.umbraco.com/apidocs/v8/ui/"
|
||||
$IndexPath = "$src\Umbraco.Web.UI.Client\docs\api\index.html"
|
||||
$IndexPath = "./api/index.html"
|
||||
(Get-Content $IndexPath).replace('location.href.replace(rUrl, indexFile)', "`'" + $BaseUrl + "`'") | Set-Content $IndexPath
|
||||
|
||||
# zip it
|
||||
& $this.BuildEnv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Client\docs\api\*.*"
|
||||
& $this.BuildEnv.Zip a -tzip -r "$out\ui-docs.zip" "$src\Umbraco.Web.UI.Docs\api\*.*"
|
||||
})
|
||||
|
||||
$ubuild.DefineMethod("Build",
|
||||
|
||||
@@ -1,65 +1,11 @@
|
||||
body {
|
||||
color: rgba(0,0,0,.8);
|
||||
}
|
||||
.navbar-inverse {
|
||||
background: #a3db78;
|
||||
}
|
||||
.navbar-inverse .navbar-nav>li>a, .navbar-inverse .navbar-text {
|
||||
color: rgba(0,0,0,.8);
|
||||
}
|
||||
|
||||
.navbar-inverse {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
.sidetoc {
|
||||
background-color: #f5fbf1;
|
||||
}
|
||||
body .toc {
|
||||
background-color: #f5fbf1;
|
||||
}
|
||||
.sidefilter {
|
||||
background-color: #daf0c9;
|
||||
}
|
||||
.subnav {
|
||||
background-color: #f5fbf1;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav>.active>a {
|
||||
color: rgba(0,0,0,.8);
|
||||
background-color: #daf0c9;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav>.active>a:focus, .navbar-inverse .navbar-nav>.active>a:hover {
|
||||
color: rgba(0,0,0,.8);
|
||||
background-color: #daf0c9;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
color: rgba(0,0,0,.8);
|
||||
background-color: #fff;
|
||||
border-color: rgba(0,0,0,.8);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background-color: #daf0c9;
|
||||
color: rgba(0,0,0,.8);
|
||||
border-color: rgba(0,0,0,.8);
|
||||
}
|
||||
|
||||
.toc .nav > li > a {
|
||||
color: rgba(0,0,0,.8);
|
||||
}
|
||||
|
||||
button, a {
|
||||
color: #f36f21;
|
||||
}
|
||||
|
||||
button:hover,
|
||||
button:focus,
|
||||
a:hover,
|
||||
a:focus {
|
||||
color: #143653;
|
||||
text-decoration: none;
|
||||
color: #34393e;
|
||||
font-family: 'Roboto', sans-serif;
|
||||
line-height: 1.5;
|
||||
font-size: 16px;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
word-wrap: break-word
|
||||
}
|
||||
|
||||
.navbar-header .navbar-brand {
|
||||
@@ -68,6 +14,202 @@ a:focus {
|
||||
width:50px;
|
||||
}
|
||||
|
||||
.toc .nav > li.active > a {
|
||||
color: #f36f21;
|
||||
#_content>a {
|
||||
margin-top: 5px;
|
||||
}
|
||||
/* HEADINGS */
|
||||
|
||||
h1 {
|
||||
font-weight: 600;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: 600;
|
||||
font-size: 24px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-weight: 600;
|
||||
font-size: 20px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-size: 14px;
|
||||
padding: 10px 0px;
|
||||
}
|
||||
|
||||
article h1,
|
||||
article h2,
|
||||
article h3,
|
||||
article h4 {
|
||||
margin-top: 35px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
article h4 {
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 2px solid #ddd;
|
||||
}
|
||||
|
||||
/* NAVBAR */
|
||||
|
||||
.navbar-brand>img {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
border: none;
|
||||
/* Both navbars use box-shadow */
|
||||
-webkit-box-shadow: 0px 1px 3px 0px rgba(100, 100, 100, 0.5);
|
||||
-moz-box-shadow: 0px 1px 3px 0px rgba(100, 100, 100, 0.5);
|
||||
box-shadow: 0px 1px 3px 0px rgba(100, 100, 100, 0.5);
|
||||
}
|
||||
|
||||
.subnav {
|
||||
border-top: 1px solid #ddd;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.navbar-inverse {
|
||||
background-color: #303ea1;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav>li>a,
|
||||
.navbar-inverse .navbar-text {
|
||||
color: #fff;
|
||||
background-color: #303ea1;
|
||||
border-bottom: 3px solid transparent;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav>li>a:focus,
|
||||
.navbar-inverse .navbar-nav>li>a:hover {
|
||||
color: #fff;
|
||||
background-color: #303ea1;
|
||||
border-bottom: 3px solid white;
|
||||
}
|
||||
|
||||
.navbar-inverse .navbar-nav>.active>a,
|
||||
.navbar-inverse .navbar-nav>.active>a:focus,
|
||||
.navbar-inverse .navbar-nav>.active>a:hover {
|
||||
color: #fff;
|
||||
background-color: #303ea1;
|
||||
border-bottom: 3px solid white;
|
||||
}
|
||||
|
||||
.navbar-form .form-control {
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
/* SIDEBAR */
|
||||
|
||||
.toc .level1>li {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.toc .nav>li>a {
|
||||
color: #34393e;
|
||||
}
|
||||
|
||||
.sidefilter {
|
||||
background-color: #fff;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.sidefilter {
|
||||
background-color: #fff;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.toc-filter {
|
||||
padding: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.toc-filter>input {
|
||||
border: 2px solid #ddd;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.toc-filter>.filter-icon {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidetoc>.toc {
|
||||
background-color: #fff;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.sidetoc {
|
||||
background-color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* ALERTS */
|
||||
|
||||
.alert {
|
||||
padding: 0px 0px 5px 0px;
|
||||
color: inherit;
|
||||
background-color: inherit;
|
||||
border: none;
|
||||
box-shadow: 0px 2px 2px 0px rgba(100, 100, 100, 0.4);
|
||||
}
|
||||
|
||||
.alert>p {
|
||||
margin-bottom: 0;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
.alert>ul {
|
||||
margin-bottom: 0;
|
||||
padding: 5px 40px;
|
||||
}
|
||||
|
||||
.alert>h5 {
|
||||
padding: 10px 15px;
|
||||
margin-top: 0;
|
||||
text-transform: uppercase;
|
||||
font-weight: bold;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
|
||||
.alert-info>h5 {
|
||||
color: #1976d2;
|
||||
border-bottom: 4px solid #1976d2;
|
||||
background-color: #e3f2fd;
|
||||
}
|
||||
|
||||
.alert-warning>h5 {
|
||||
color: #f57f17;
|
||||
border-bottom: 4px solid #f57f17;
|
||||
background-color: #fff3e0;
|
||||
}
|
||||
|
||||
.alert-danger>h5 {
|
||||
color: #d32f2f;
|
||||
border-bottom: 4px solid #d32f2f;
|
||||
background-color: #ffebee;
|
||||
}
|
||||
|
||||
/* CODE HIGHLIGHT */
|
||||
pre {
|
||||
padding: 9.5px;
|
||||
margin: 0 0 10px;
|
||||
font-size: 13px;
|
||||
word-break: break-all;
|
||||
word-wrap: break-word;
|
||||
background-color: #fffaef;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0px 1px 4px 1px rgba(100, 100, 100, 0.4);
|
||||
}
|
||||
|
||||
.sideaffix {
|
||||
overflow: visible;
|
||||
}
|
||||
+3
-3
@@ -2,7 +2,7 @@
|
||||
using System.Resources;
|
||||
|
||||
[assembly: AssemblyCompany("Umbraco")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2019")]
|
||||
[assembly: AssemblyCopyright("Copyright © Umbraco 2020")]
|
||||
[assembly: AssemblyTrademark("")]
|
||||
[assembly: AssemblyCulture("")]
|
||||
|
||||
@@ -18,5 +18,5 @@ using System.Resources;
|
||||
[assembly: AssemblyVersion("8.0.0")]
|
||||
|
||||
// these are FYI and changed automatically
|
||||
[assembly: AssemblyFileVersion("8.1.0")]
|
||||
[assembly: AssemblyInformationalVersion("8.1.0")]
|
||||
[assembly: AssemblyFileVersion("8.7.0")]
|
||||
[assembly: AssemblyInformationalVersion("8.7.0")]
|
||||
|
||||
@@ -11,5 +11,6 @@
|
||||
public const string TemplateFrontEndCacheKey = "template";
|
||||
|
||||
public const string MacroContentCacheKey = "macroContent_"; // used in MacroRenderers
|
||||
public const string MacroFromAliasCacheKey = "macroFromAlias_";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,23 +37,13 @@ namespace Umbraco.Core.Cache
|
||||
/// <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);
|
||||
return GetInternal(key, factory, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
InsertInternal(key, factory, timeout, isSliding, priority, removedCallback, dependentFiles);
|
||||
}
|
||||
|
||||
#region Dictionary
|
||||
@@ -86,7 +76,7 @@ namespace Umbraco.Core.Cache
|
||||
|
||||
protected override void EnterWriteLock()
|
||||
{
|
||||
_locker.EnterWriteLock();;
|
||||
_locker.EnterWriteLock();
|
||||
}
|
||||
|
||||
protected override void ExitReadLock()
|
||||
@@ -103,7 +93,7 @@ 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)
|
||||
private object GetInternal(string key, Func<object> factory, TimeSpan? timeout, bool isSliding = false, CacheItemPriority priority = CacheItemPriority.Normal, CacheItemRemovedCallback removedCallback = null, string[] dependentFiles = null)
|
||||
{
|
||||
key = GetCacheKey(key);
|
||||
|
||||
@@ -163,6 +153,10 @@ namespace Umbraco.Core.Cache
|
||||
var sliding = isSliding == false ? System.Web.Caching.Cache.NoSlidingExpiration : (timeout ?? System.Web.Caching.Cache.NoSlidingExpiration);
|
||||
|
||||
lck.UpgradeToWriteLock();
|
||||
|
||||
// create a cache dependency if one is needed.
|
||||
var dependency = dependentFiles != null && dependentFiles.Length > 0 ? new CacheDependency(dependentFiles) : null;
|
||||
|
||||
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
|
||||
_cache.Insert(key, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
@@ -180,7 +174,7 @@ 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)
|
||||
private void InsertInternal(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.
|
||||
@@ -197,6 +191,10 @@ namespace Umbraco.Core.Cache
|
||||
try
|
||||
{
|
||||
_locker.EnterWriteLock();
|
||||
|
||||
// create a cache dependency if one is needed.
|
||||
var dependency = dependentFiles != null && dependentFiles.Length > 0 ? new CacheDependency(dependentFiles) : null;
|
||||
|
||||
//NOTE: 'Insert' on System.Web.Caching.Cache actually does an add or update!
|
||||
_cache.Insert(cacheKey, result, dependency, absolute, sliding, priority, removedCallback);
|
||||
}
|
||||
|
||||
@@ -70,7 +70,23 @@ 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
|
||||
{
|
||||
try
|
||||
{
|
||||
_instanceLocker.EnterReadLock();
|
||||
return _innerSet.Count;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_instanceLocker.IsReadLockHeld)
|
||||
_instanceLocker.ExitReadLock();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the <see cref="T:System.Collections.Generic.ICollection`1"/> is read-only.
|
||||
@@ -105,8 +121,7 @@ namespace Umbraco.Core.Collections
|
||||
/// <returns></returns>
|
||||
public bool TryAdd(T item)
|
||||
{
|
||||
var clone = GetThreadSafeClone();
|
||||
if (clone.Contains(item)) return false;
|
||||
if (Contains(item)) return false;
|
||||
try
|
||||
{
|
||||
_instanceLocker.EnterWriteLock();
|
||||
@@ -150,7 +165,16 @@ namespace Umbraco.Core.Collections
|
||||
/// <param name="item">The object to locate in the <see cref="T:System.Collections.Generic.ICollection`1"/>.</param>
|
||||
public bool Contains(T item)
|
||||
{
|
||||
return GetThreadSafeClone().Contains(item);
|
||||
try
|
||||
{
|
||||
_instanceLocker.EnterReadLock();
|
||||
return _innerSet.Contains(item);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_instanceLocker.IsReadLockHeld)
|
||||
_instanceLocker.ExitReadLock();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -168,13 +192,13 @@ namespace Umbraco.Core.Collections
|
||||
HashSet<T> clone = null;
|
||||
try
|
||||
{
|
||||
_instanceLocker.EnterWriteLock();
|
||||
_instanceLocker.EnterReadLock();
|
||||
clone = new HashSet<T>(_innerSet, _innerSet.Comparer);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (_instanceLocker.IsWriteLockHeld)
|
||||
_instanceLocker.ExitWriteLock();
|
||||
if (_instanceLocker.IsReadLockHeld)
|
||||
_instanceLocker.ExitReadLock();
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Collections
|
||||
{
|
||||
@@ -26,7 +27,7 @@ namespace Umbraco.Core.Collections
|
||||
/// <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)
|
||||
{
|
||||
KeySelector = keySelector ?? throw new ArgumentException("keySelector");
|
||||
KeySelector = keySelector ?? throw new ArgumentException(nameof(keySelector));
|
||||
Indecies = new Dictionary<TKey, int>(equalityComparer);
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ namespace Umbraco.Core.Collections
|
||||
{
|
||||
var key = KeySelector(item);
|
||||
if (Indecies.ContainsKey(key))
|
||||
throw new DuplicateKeyException(key.ToString());
|
||||
throw new ArgumentException($"An element with the same key '{key}' already exists in the dictionary.", nameof(item));
|
||||
|
||||
if (index != Count)
|
||||
{
|
||||
@@ -91,7 +92,7 @@ namespace Umbraco.Core.Collections
|
||||
{
|
||||
//confirm key matches
|
||||
if (!KeySelector(value).Equals(key))
|
||||
throw new InvalidOperationException("Key of new value does not match");
|
||||
throw new InvalidOperationException("Key of new value does not match.");
|
||||
|
||||
if (!Indecies.ContainsKey(key))
|
||||
{
|
||||
@@ -118,7 +119,7 @@ namespace Umbraco.Core.Collections
|
||||
|
||||
//confirm key matches
|
||||
if (!KeySelector(value).Equals(key))
|
||||
throw new InvalidOperationException("Key of new value does not match");
|
||||
throw new InvalidOperationException("Key of new value does not match.");
|
||||
|
||||
this[Indecies[key]] = value;
|
||||
return true;
|
||||
@@ -155,12 +156,12 @@ namespace Umbraco.Core.Collections
|
||||
{
|
||||
if (!Indecies.ContainsKey(currentKey))
|
||||
{
|
||||
throw new InvalidOperationException("No item with the key " + currentKey + "was found in the collection");
|
||||
throw new InvalidOperationException($"No item with the key '{currentKey}' was found in the dictionary.");
|
||||
}
|
||||
|
||||
if (ContainsKey(newKey))
|
||||
{
|
||||
throw new DuplicateKeyException(newKey.ToString());
|
||||
throw new ArgumentException($"An element with the same key '{newKey}' already exists in the dictionary.", nameof(newKey));
|
||||
}
|
||||
|
||||
var currentIndex = Indecies[currentKey];
|
||||
@@ -234,16 +235,5 @@ namespace Umbraco.Core.Collections
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal class DuplicateKeyException : Exception
|
||||
{
|
||||
public DuplicateKeyException(string key)
|
||||
: base("Attempted to insert duplicate key \"" + key + "\" in collection.")
|
||||
{
|
||||
Key = key;
|
||||
}
|
||||
|
||||
public string Key { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace Umbraco.Core.Collections
|
||||
if (_items.TryGetValue(key, out value))
|
||||
yield return value;
|
||||
else if (throwOnMissing)
|
||||
throw new Exception(MissingDependencyError);
|
||||
throw new Exception($"{MissingDependencyError} Error in type {typeof(TItem).Name}, with key {key}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,21 +44,22 @@ namespace Umbraco.Core.Compose
|
||||
public void Terminate()
|
||||
{ }
|
||||
|
||||
internal static IUser UnknownUser => new User { Id = Constants.Security.UnknownUserId, Name = Constants.Security.UnknownUserName, Email = "" };
|
||||
|
||||
private IUser CurrentPerformingUser
|
||||
{
|
||||
get
|
||||
{
|
||||
var identity = Thread.CurrentPrincipal?.GetUmbracoIdentity();
|
||||
return identity == null
|
||||
? new User { Id = 0, Name = "SYSTEM", Email = "" }
|
||||
: _userService.GetUserById(Convert.ToInt32(identity.Id));
|
||||
var user = identity == null ? null : _userService.GetUserById(Convert.ToInt32(identity.Id));
|
||||
return user ?? UnknownUser;
|
||||
}
|
||||
}
|
||||
|
||||
private IUser GetPerformingUser(int userId)
|
||||
{
|
||||
var found = userId >= 0 ? _userService.GetUserById(userId) : null;
|
||||
return found ?? new User {Id = 0, Name = "SYSTEM", Email = ""};
|
||||
return found ?? UnknownUser;
|
||||
}
|
||||
|
||||
private string PerformingIp
|
||||
|
||||
@@ -26,10 +26,11 @@ namespace Umbraco.Core.Compose
|
||||
|
||||
if (relationType == null)
|
||||
{
|
||||
relationType = new RelationType(Constants.ObjectTypes.Document,
|
||||
relationType = new RelationType(Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias,
|
||||
Constants.Conventions.RelationTypes.RelateDocumentOnCopyName,
|
||||
true,
|
||||
Constants.ObjectTypes.Document,
|
||||
Constants.Conventions.RelationTypes.RelateDocumentOnCopyAlias,
|
||||
Constants.Conventions.RelationTypes.RelateDocumentOnCopyName) { IsBidirectional = true };
|
||||
Constants.ObjectTypes.Document);
|
||||
|
||||
relationService.Save(relationType);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Umbraco.Core.Compose
|
||||
var documentObjectType = Constants.ObjectTypes.Document;
|
||||
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteName;
|
||||
|
||||
relationType = new RelationType(documentObjectType, documentObjectType, relationTypeAlias, relationTypeName);
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
|
||||
relationService.Save(relationType);
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ namespace Umbraco.Core.Compose
|
||||
{
|
||||
var documentObjectType = Constants.ObjectTypes.Document;
|
||||
const string relationTypeName = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteName;
|
||||
relationType = new RelationType(documentObjectType, documentObjectType, relationTypeAlias, relationTypeName);
|
||||
relationType = new RelationType(relationTypeName, relationTypeAlias, false, documentObjectType, documentObjectType);
|
||||
relationService.Save(relationType);
|
||||
}
|
||||
foreach (var item in e.MoveInfoCollection)
|
||||
|
||||
@@ -79,9 +79,12 @@ namespace Umbraco.Core.Composing
|
||||
foreach (var type in types)
|
||||
EnsureType(type, "register");
|
||||
|
||||
// register them
|
||||
// register them - ensuring that each item is registered with the same lifetime as the collection.
|
||||
// NOTE: Previously each one was not registered with the same lifetime which would mean that if there
|
||||
// was a dependency on an individual item, it would resolve a brand new transient instance which isn't what
|
||||
// we would expect to happen. The same item should be resolved from the container as the collection.
|
||||
foreach (var type in types)
|
||||
register.Register(type);
|
||||
register.Register(type, CollectionLifetime);
|
||||
|
||||
_registeredTypes = types;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
@@ -43,8 +44,15 @@ namespace Umbraco.Core.Composing
|
||||
var componentType = component.GetType();
|
||||
using (_logger.DebugDuration<ComponentCollection>($"Terminating {componentType.FullName}.", $"Terminated {componentType.FullName}.", thresholdMilliseconds: LogThresholdMilliseconds))
|
||||
{
|
||||
component.Terminate();
|
||||
component.DisposeIfDisposable();
|
||||
try
|
||||
{
|
||||
component.Terminate();
|
||||
component.DisposeIfDisposable();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.Error<ComponentCollection>(ex, "Error while terminating component {ComponentType}.", componentType.FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,19 +18,52 @@ namespace Umbraco.Core.Composing
|
||||
private readonly Composition _composition;
|
||||
private readonly IProfilingLogger _logger;
|
||||
private readonly IEnumerable<Type> _composerTypes;
|
||||
private readonly IEnumerable<Attribute> _enableDisableAttributes;
|
||||
|
||||
private const int LogThresholdMilliseconds = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Composers"/> class.
|
||||
/// Initializes a new instance of the <see cref="Composers" /> class.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <param name="composerTypes">The composer types.</param>
|
||||
/// <param name="logger">A profiling logger.</param>
|
||||
/// <param name="composerTypes">The <see cref="IComposer" /> types.</param>
|
||||
/// <param name="logger">The profiling logger.</param>
|
||||
[Obsolete("This overload only gets the EnableComposer/DisableComposer attributes from the composerTypes assemblies.")]
|
||||
public Composers(Composition composition, IEnumerable<Type> composerTypes, IProfilingLogger logger)
|
||||
: this(composition, composerTypes, Enumerable.Empty<Attribute>(), logger)
|
||||
{
|
||||
var enableDisableAttributes = new List<Attribute>();
|
||||
|
||||
var assemblies = composerTypes.Select(t => t.Assembly).Distinct();
|
||||
foreach (var assembly in assemblies)
|
||||
{
|
||||
enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(EnableComposerAttribute)));
|
||||
enableDisableAttributes.AddRange(assembly.GetCustomAttributes(typeof(DisableComposerAttribute)));
|
||||
}
|
||||
|
||||
_enableDisableAttributes = enableDisableAttributes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Composers" /> class.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
/// <param name="composerTypes">The <see cref="IComposer" /> types.</param>
|
||||
/// <param name="enableDisableAttributes">The <see cref="EnableComposerAttribute" /> and/or <see cref="DisableComposerAttribute" /> attributes.</param>
|
||||
/// <param name="logger">The profiling logger.</param>
|
||||
/// <exception cref="ArgumentNullException">composition
|
||||
/// or
|
||||
/// composerTypes
|
||||
/// or
|
||||
/// enableDisableAttributes
|
||||
/// or
|
||||
/// logger</exception>
|
||||
|
||||
public Composers(Composition composition, IEnumerable<Type> composerTypes, IEnumerable<Attribute> enableDisableAttributes, IProfilingLogger logger)
|
||||
{
|
||||
_composition = composition ?? throw new ArgumentNullException(nameof(composition));
|
||||
_composerTypes = composerTypes ?? throw new ArgumentNullException(nameof(composerTypes));
|
||||
_enableDisableAttributes = enableDisableAttributes ?? throw new ArgumentNullException(nameof(enableDisableAttributes));
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
|
||||
@@ -103,7 +136,7 @@ namespace Umbraco.Core.Composing
|
||||
.ToList();
|
||||
|
||||
// enable or disable composers
|
||||
EnableDisableComposers(composerTypeList);
|
||||
EnableDisableComposers(_enableDisableAttributes, composerTypeList);
|
||||
|
||||
void GatherInterfaces<TAttribute>(Type type, Func<TAttribute, Type> getTypeInAttribute, HashSet<Type> iset, List<Type> set2)
|
||||
where TAttribute : Attribute
|
||||
@@ -218,7 +251,7 @@ namespace Umbraco.Core.Composing
|
||||
return text.ToString();
|
||||
}
|
||||
|
||||
private static void EnableDisableComposers(ICollection<Type> types)
|
||||
private static void EnableDisableComposers(IEnumerable<Attribute> enableDisableAttributes, ICollection<Type> types)
|
||||
{
|
||||
var enabled = new Dictionary<Type, EnableInfo>();
|
||||
|
||||
@@ -240,20 +273,16 @@ namespace Umbraco.Core.Composing
|
||||
enableInfo.Weight = weight2;
|
||||
}
|
||||
|
||||
var assemblies = types.Select(x => x.Assembly).Distinct();
|
||||
foreach (var assembly in assemblies)
|
||||
foreach (var attr in enableDisableAttributes.OfType<EnableComposerAttribute>())
|
||||
{
|
||||
foreach (var attr in assembly.GetCustomAttributes<EnableComposerAttribute>())
|
||||
{
|
||||
var type = attr.EnabledType;
|
||||
UpdateEnableInfo(type, 2, enabled, true);
|
||||
}
|
||||
var type = attr.EnabledType;
|
||||
UpdateEnableInfo(type, 2, enabled, true);
|
||||
}
|
||||
|
||||
foreach (var attr in assembly.GetCustomAttributes<DisableComposerAttribute>())
|
||||
{
|
||||
var type = attr.DisabledType;
|
||||
UpdateEnableInfo(type, 2, enabled, false);
|
||||
}
|
||||
foreach (var attr in enableDisableAttributes.OfType<DisableComposerAttribute>())
|
||||
{
|
||||
var type = attr.DisabledType;
|
||||
UpdateEnableInfo(type, 2, enabled, false);
|
||||
}
|
||||
|
||||
foreach (var composerType in types)
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
|
||||
// register the IFileSystem supporting the IMediaFileSystem
|
||||
// THIS IS THE ONLY THING THAT NEEDS TO CHANGE, IN ORDER TO REPLACE THE UNDERLYING FILESYSTEM
|
||||
// and, SupportingFileSystem.For<IMediaFileSystem>() returns the underlying filesystem
|
||||
composition.SetMediaFileSystem(() => new PhysicalFileSystem("~/media"));
|
||||
composition.SetMediaFileSystem(() => new PhysicalFileSystem(SystemDirectories.Media));
|
||||
|
||||
return composition;
|
||||
}
|
||||
|
||||
@@ -47,6 +47,8 @@ namespace Umbraco.Core.Composing.CompositionExtensions
|
||||
composition.RegisterUnique<IScriptRepository, ScriptRepository>();
|
||||
composition.RegisterUnique<IStylesheetRepository, StylesheetRepository>();
|
||||
composition.RegisterUnique<IContentTypeCommonRepository, ContentTypeCommonRepository>();
|
||||
composition.RegisterUnique<IInstallationRepository, InstallationRepository>();
|
||||
composition.RegisterUnique<IUpgradeCheckRepository, UpgradeCheckRepository>();
|
||||
|
||||
return composition;
|
||||
}
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace Umbraco.Core.Composing.CompositionExtensions
|
||||
var pluginLangFolders = appPlugins.Exists == false
|
||||
? Enumerable.Empty<LocalizedTextServiceSupplementaryFileSource>()
|
||||
: appPlugins.GetDirectories()
|
||||
.SelectMany(x => x.GetDirectories("Lang"))
|
||||
.SelectMany(x => x.GetDirectories("Lang", SearchOption.AllDirectories))
|
||||
.SelectMany(x => x.GetFiles("*.xml", SearchOption.TopDirectoryOnly))
|
||||
.Select(x => new LocalizedTextServiceSupplementaryFileSource(x, false));
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Mapping;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.PackageActions;
|
||||
using Umbraco.Core.Packaging;
|
||||
@@ -154,6 +155,9 @@ namespace Umbraco.Core.Composing
|
||||
public static DataEditorCollection DataEditors
|
||||
=> Factory.GetInstance<DataEditorCollection>();
|
||||
|
||||
public static DataValueReferenceFactoryCollection DataValueReferenceFactories
|
||||
=> Factory.GetInstance<DataValueReferenceFactoryCollection>();
|
||||
|
||||
public static PropertyEditorCollection PropertyEditors
|
||||
=> Factory.GetInstance<PropertyEditorCollection>();
|
||||
|
||||
@@ -205,6 +209,8 @@ namespace Umbraco.Core.Composing
|
||||
public static IVariationContextAccessor VariationContextAccessor
|
||||
=> Factory.GetInstance<IVariationContextAccessor>();
|
||||
|
||||
public static IImageUrlGenerator ImageUrlGenerator
|
||||
=> Factory.GetInstance<IImageUrlGenerator>();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,31 +12,62 @@
|
||||
/// or MS.DI, PerDependency in Autofac.</remarks>
|
||||
Transient,
|
||||
|
||||
// TODO: We need to fix this up, currently LightInject is the only one that behaves differently from all other containers.
|
||||
// ... the simple fix would be to map this to PerScopeLifetime in LI but need to wait on a response here https://github.com/seesharper/LightInject/issues/494#issuecomment-518942625
|
||||
//
|
||||
// we use it for controllers, httpContextBase and other request scoped objects: MembershpHelper, TagQuery, UmbracoTreeSearcher and ISearchableTree
|
||||
// - so that they are automatically disposed at the end of the scope (ie request)
|
||||
// - not sure they should not be simply 'scoped'?
|
||||
|
||||
/// <summary>
|
||||
/// One unique instance per request.
|
||||
/// </summary>
|
||||
// TODO: review lifetimes for LightInject vs other containers
|
||||
// currently, corresponds to 'Request' in LightInject which is 'Transient + disposed by Scope'
|
||||
// but NOT (in LightInject) a per-web-request lifetime, more a TransientScoped
|
||||
//
|
||||
// we use it for controllers, httpContextBase and umbracoContext
|
||||
// - so that they are automatically disposed at the end of the scope (ie request)
|
||||
// - not sure they should not be simply 'scoped'?
|
||||
//
|
||||
// Castle has an extra PerWebRequest something, and others use scope
|
||||
// what about Request before first request ie during application startup?
|
||||
// see http://blog.ploeh.dk/2009/11/17/UsingCastleWindsor'sPerWebRequestlifestylewithASP.NETMVConIIS7/
|
||||
// Castle ends up requiring a special scope manager too
|
||||
// see https://groups.google.com/forum/#!topic/castle-project-users/1E2W9LVIYR4
|
||||
//
|
||||
// but maybe also - why are we requiring scoped services at startup?
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Any instance created with this lifetime will be disposed at the end of a request.
|
||||
/// </para>
|
||||
/// Corresponds to
|
||||
/// <para>
|
||||
/// PerRequestLifeTime in LightInject - means transient but disposed at the end of the current web request.
|
||||
/// see: https://github.com/seesharper/LightInject/issues/494#issuecomment-518493262
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Scoped in MS.DI - means one per web request.
|
||||
/// see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2#service-lifetimes</para>
|
||||
/// <para>
|
||||
/// InstancePerRequest in Autofac - means one per web request.
|
||||
/// see https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-request
|
||||
/// But "Behind the scenes, though, it’s still just instance per matching lifetime scope."
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// LifestylePerWebRequest in Castle Windsor - means one per web request.
|
||||
/// see https://github.com/castleproject/Windsor/blob/master/docs/mvc-tutorial-part-7-lifestyles.md#the-perwebrequest-lifestyle
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
Request,
|
||||
|
||||
/// <summary>
|
||||
/// One unique instance per container scope.
|
||||
/// One unique instance per scope.
|
||||
/// </summary>
|
||||
/// <remarks>Corresponds to Scope in LightInject, Scoped in MS.DI
|
||||
/// or Castle Windsor, PerLifetimeScope in Autofac.</remarks>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Any instance created with this lifetime will be disposed at the end of the current scope.
|
||||
/// </para>
|
||||
/// Corresponds to
|
||||
/// <para>PerScopeLifetime in LightInject (when in a request, means one per web request)</para>
|
||||
/// <para>
|
||||
/// Scoped in MS.DI (when in a request, means one per web request)
|
||||
/// see https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2#service-lifetimes</para>
|
||||
/// <para>
|
||||
/// InstancePerLifetimeScope in Autofac (when in a request, means one per web request)
|
||||
/// see https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-lifetime-scope
|
||||
/// Also note that Autofac's InstancePerRequest is the same as this, see https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-request
|
||||
/// it says "Behind the scenes, though, it’s still just instance per matching lifetime scope."
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// LifestyleScoped in Castle Windsor
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
Scope,
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Composing.LightInject
|
||||
@@ -6,20 +7,51 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
/// <summary>
|
||||
/// Represents errors that occur due to LightInject.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class LightInjectException : Exception
|
||||
{
|
||||
public LightInjectException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
public LightInjectException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
private const string LightInjectUnableToResolveType = "Unable to resolve type:";
|
||||
private const string LightInjectUnresolvedDependency = "Unresolved dependency ";
|
||||
private const string LightInjectRequestedDependency = "[Requested dependency:";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LightInjectException" /> class.
|
||||
/// </summary>
|
||||
public LightInjectException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LightInjectException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public LightInjectException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LightInjectException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public LightInjectException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="LightInjectException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected LightInjectException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Tries to throw the exception with additional details.
|
||||
/// </summary>
|
||||
/// <param name="e">The exception.</param>
|
||||
/// <exception cref="Umbraco.Core.Composing.LightInject.LightInjectException"></exception>
|
||||
public static void TryThrow(Exception e)
|
||||
{
|
||||
var ex = e as InvalidOperationException;
|
||||
@@ -32,6 +64,12 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
throw new LightInjectException(sb.ToString(), e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tries to throw the exception with additional details.
|
||||
/// </summary>
|
||||
/// <param name="e">The exception.</param>
|
||||
/// <param name="implementingType">The implementing type.</param>
|
||||
/// <exception cref="Umbraco.Core.Composing.LightInject.LightInjectException"></exception>
|
||||
public static void TryThrow(Exception e, Type implementingType)
|
||||
{
|
||||
var ex = e as InvalidOperationException;
|
||||
@@ -45,6 +83,11 @@ namespace Umbraco.Core.Composing.LightInject
|
||||
throw new LightInjectException(sb.ToString(), e);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the details.
|
||||
/// </summary>
|
||||
/// <param name="ex">The exception.</param>
|
||||
/// <param name="sb">The <see cref="StringBuilder" /> to write the details to.</param>
|
||||
private static void WriteDetails(InvalidOperationException ex, StringBuilder sb)
|
||||
{
|
||||
ex = ex.InnerException as InvalidOperationException;
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
@@ -42,8 +43,8 @@ namespace Umbraco.Core.Composing
|
||||
private string _currentAssembliesHash;
|
||||
private IEnumerable<Assembly> _assemblies;
|
||||
private bool _reportedChange;
|
||||
private static string _localTempPath;
|
||||
private static string _fileBasePath;
|
||||
private readonly string _localTempPath;
|
||||
private string _fileBasePath;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TypeLoader"/> class.
|
||||
@@ -505,6 +506,49 @@ namespace Umbraco.Core.Composing
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get Assembly Attributes
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly attributes of the specified type <typeparamref name="T" />.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The attribute type.</typeparam>
|
||||
/// <returns>
|
||||
/// The assembly attributes of the specified type <typeparamref name="T" />.
|
||||
/// </returns>
|
||||
public IEnumerable<T> GetAssemblyAttributes<T>()
|
||||
where T : Attribute
|
||||
{
|
||||
return AssembliesToScan.SelectMany(a => a.GetCustomAttributes<T>()).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the assembly attributes.
|
||||
/// </summary>
|
||||
/// <returns>
|
||||
/// All assembly attributes.
|
||||
/// </returns>
|
||||
public IEnumerable<Attribute> GetAssemblyAttributes()
|
||||
{
|
||||
return AssembliesToScan.SelectMany(a => a.GetCustomAttributes()).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the assembly attributes of the specified <paramref name="attributeTypes" />.
|
||||
/// </summary>
|
||||
/// <param name="attributeTypes">The attribute types.</param>
|
||||
/// <returns>
|
||||
/// The assembly attributes of the specified types.
|
||||
/// </returns>
|
||||
/// <exception cref="ArgumentNullException">attributeTypes</exception>
|
||||
public IEnumerable<Attribute> GetAssemblyAttributes(params Type[] attributeTypes)
|
||||
{
|
||||
if (attributeTypes == null) throw new ArgumentNullException(nameof(attributeTypes));
|
||||
|
||||
return AssembliesToScan.SelectMany(a => attributeTypes.SelectMany(at => a.GetCustomAttributes(at))).ToList();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Get Types
|
||||
|
||||
/// <summary>
|
||||
@@ -813,11 +857,44 @@ namespace Umbraco.Core.Composing
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents the error that occurs when a type was not found in the cache type
|
||||
/// list with the specified TypeResolutionKind.
|
||||
/// Represents the error that occurs when a type was not found in the cache type list with the specified TypeResolutionKind.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
internal class CachedTypeNotFoundInFileException : Exception
|
||||
{ }
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CachedTypeNotFoundInFileException" /> class.
|
||||
/// </summary>
|
||||
public CachedTypeNotFoundInFileException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CachedTypeNotFoundInFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public CachedTypeNotFoundInFileException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CachedTypeNotFoundInFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public CachedTypeNotFoundInFileException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CachedTypeNotFoundInFileException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected CachedTypeNotFoundInFileException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Dictionary;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging.Viewer;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.Models.PublishedContent;
|
||||
using Umbraco.Core.PackageActions;
|
||||
using Umbraco.Core.Persistence.Mappers;
|
||||
@@ -48,6 +49,13 @@ namespace Umbraco.Core
|
||||
public static DataEditorCollectionBuilder DataEditors(this Composition composition)
|
||||
=> composition.WithCollectionBuilder<DataEditorCollectionBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data value reference factory collection builder.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
public static DataValueReferenceFactoryCollectionBuilder DataValueReferenceFactories(this Composition composition)
|
||||
=> composition.WithCollectionBuilder<DataValueReferenceFactoryCollectionBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the property value converters collection builder.
|
||||
/// </summary>
|
||||
@@ -66,9 +74,16 @@ namespace Umbraco.Core
|
||||
/// Gets the validators collection builder.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
internal static ManifestValueValidatorCollectionBuilder Validators(this Composition composition)
|
||||
internal static ManifestValueValidatorCollectionBuilder ManifestValueValidators(this Composition composition)
|
||||
=> composition.WithCollectionBuilder<ManifestValueValidatorCollectionBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the manifest filter collection builder.
|
||||
/// </summary>
|
||||
/// <param name="composition">The composition.</param>
|
||||
public static ManifestFilterCollectionBuilder ManifestFilters(this Composition composition)
|
||||
=> composition.WithCollectionBuilder<ManifestFilterCollectionBuilder>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the components collection builder.
|
||||
/// </summary>
|
||||
|
||||
@@ -7,6 +7,7 @@ using Umbraco.Core.Configuration.HealthChecks;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Manifest;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
@@ -41,7 +42,12 @@ namespace Umbraco.Core
|
||||
configs.Add(() => new CoreDebug());
|
||||
|
||||
// GridConfig depends on runtime caches, manifest parsers... and cannot be available during composition
|
||||
configs.Add<IGridConfig>(factory => new GridConfig(factory.GetInstance<ILogger>(), factory.GetInstance<AppCaches>(), configDir, factory.GetInstance<IRuntimeState>().Debug));
|
||||
configs.Add<IGridConfig>(factory => new GridConfig(
|
||||
factory.GetInstance<ILogger>(),
|
||||
factory.GetInstance<AppCaches>(),
|
||||
configDir,
|
||||
factory.GetInstance<ManifestParser>(),
|
||||
factory.GetInstance<IRuntimeState>().Debug));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace Umbraco.Core.Configuration
|
||||
/// </summary>
|
||||
private static void ResetInternal()
|
||||
{
|
||||
GlobalSettingsExtensions.Reset();
|
||||
_reservedPaths = null;
|
||||
_reservedUrls = null;
|
||||
HasSmtpServer = null;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web;
|
||||
using System.Web.Routing;
|
||||
using Umbraco.Core.IO;
|
||||
@@ -9,22 +11,9 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
public static class GlobalSettingsExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Used in unit testing to reset all config items, this is automatically called by GlobalSettings.Reset()
|
||||
/// </summary>
|
||||
internal static void Reset()
|
||||
{
|
||||
_reservedUrlsCache = null;
|
||||
_mvcArea = null;
|
||||
}
|
||||
|
||||
private static readonly object Locker = new object();
|
||||
//make this volatile so that we can ensure thread safety with a double check lock
|
||||
private static volatile string _reservedUrlsCache;
|
||||
private static string _reservedPathsCache;
|
||||
private static HashSet<string> _reservedList = new HashSet<string>();
|
||||
private static string _mvcArea;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// This returns the string of the MVC Area route.
|
||||
/// </summary>
|
||||
@@ -40,6 +29,13 @@ namespace Umbraco.Core.Configuration
|
||||
{
|
||||
if (_mvcArea != null) return _mvcArea;
|
||||
|
||||
_mvcArea = GetUmbracoMvcAreaNoCache(globalSettings);
|
||||
|
||||
return _mvcArea;
|
||||
}
|
||||
|
||||
internal static string GetUmbracoMvcAreaNoCache(this IGlobalSettings globalSettings)
|
||||
{
|
||||
if (globalSettings.Path.IsNullOrWhiteSpace())
|
||||
{
|
||||
throw new InvalidOperationException("Cannot create an MVC Area path without the umbracoPath specified");
|
||||
@@ -48,95 +44,8 @@ namespace Umbraco.Core.Configuration
|
||||
var path = globalSettings.Path;
|
||||
if (path.StartsWith(SystemDirectories.Root)) // beware of TrimStart, see U4-2518
|
||||
path = path.Substring(SystemDirectories.Root.Length);
|
||||
_mvcArea = path.TrimStart('~').TrimStart('/').Replace('/', '-').Trim().ToLower();
|
||||
return _mvcArea;
|
||||
return path.TrimStart('~').TrimStart('/').Replace('/', '-').Trim().ToLower();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the specified URL is reserved or is inside a reserved path.
|
||||
/// </summary>
|
||||
/// <param name="globalSettings"></param>
|
||||
/// <param name="url">The URL to check.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the specified URL is reserved; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
internal static bool IsReservedPathOrUrl(this IGlobalSettings globalSettings, string url)
|
||||
{
|
||||
if (_reservedUrlsCache == null)
|
||||
{
|
||||
lock (Locker)
|
||||
{
|
||||
if (_reservedUrlsCache == null)
|
||||
{
|
||||
// store references to strings to determine changes
|
||||
_reservedPathsCache = globalSettings.ReservedPaths;
|
||||
_reservedUrlsCache = globalSettings.ReservedUrls;
|
||||
|
||||
// add URLs and paths to a new list
|
||||
var newReservedList = new HashSet<string>();
|
||||
foreach (var reservedUrlTrimmed in _reservedUrlsCache
|
||||
.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim().ToLowerInvariant())
|
||||
.Where(x => x.IsNullOrWhiteSpace() == false)
|
||||
.Select(reservedUrl => IOHelper.ResolveUrl(reservedUrl).Trim().EnsureStartsWith("/"))
|
||||
.Where(reservedUrlTrimmed => reservedUrlTrimmed.IsNullOrWhiteSpace() == false))
|
||||
{
|
||||
newReservedList.Add(reservedUrlTrimmed);
|
||||
}
|
||||
|
||||
foreach (var reservedPathTrimmed in _reservedPathsCache
|
||||
.Split(new[] {","}, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Select(x => x.Trim().ToLowerInvariant())
|
||||
.Where(x => x.IsNullOrWhiteSpace() == false)
|
||||
.Select(reservedPath => IOHelper.ResolveUrl(reservedPath).Trim().EnsureStartsWith("/").EnsureEndsWith("/"))
|
||||
.Where(reservedPathTrimmed => reservedPathTrimmed.IsNullOrWhiteSpace() == false))
|
||||
{
|
||||
newReservedList.Add(reservedPathTrimmed);
|
||||
}
|
||||
|
||||
// use the new list from now on
|
||||
_reservedList = newReservedList;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//The url should be cleaned up before checking:
|
||||
// * If it doesn't contain an '.' in the path then we assume it is a path based URL, if that is the case we should add an trailing '/' because all of our reservedPaths use a trailing '/'
|
||||
// * We shouldn't be comparing the query at all
|
||||
var pathPart = url.Split(new[] {'?'}, StringSplitOptions.RemoveEmptyEntries)[0].ToLowerInvariant();
|
||||
if (pathPart.Contains(".") == false)
|
||||
{
|
||||
pathPart = pathPart.EnsureEndsWith('/');
|
||||
}
|
||||
|
||||
// return true if url starts with an element of the reserved list
|
||||
return _reservedList.Any(x => pathPart.InvariantStartsWith(x));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the current request is reserved based on the route table and
|
||||
/// whether the specified URL is reserved or is inside a reserved path.
|
||||
/// </summary>
|
||||
/// <param name="globalSettings"></param>
|
||||
/// <param name="url"></param>
|
||||
/// <param name="httpContext"></param>
|
||||
/// <param name="routes">The route collection to lookup the request in</param>
|
||||
/// <returns></returns>
|
||||
internal static bool IsReservedPathOrUrl(this IGlobalSettings globalSettings, string url, HttpContextBase httpContext, RouteCollection routes)
|
||||
{
|
||||
if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));
|
||||
if (routes == null) throw new ArgumentNullException(nameof(routes));
|
||||
|
||||
//check if the current request matches a route, if so then it is reserved.
|
||||
//TODO: This value should be cached! Else this is doing double routing in MVC every request!
|
||||
var route = routes.GetRouteData(httpContext);
|
||||
if (route != null)
|
||||
return true;
|
||||
|
||||
//continue with the standard ignore routine
|
||||
return globalSettings.IsReservedPathOrUrl(url);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
using System.IO;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Manifest;
|
||||
|
||||
namespace Umbraco.Core.Configuration.Grid
|
||||
{
|
||||
class GridConfig : IGridConfig
|
||||
{
|
||||
public GridConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, bool isDebug)
|
||||
public GridConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, ManifestParser manifestParser, bool isDebug)
|
||||
{
|
||||
EditorsConfig = new GridEditorsConfig(logger, appCaches, configFolder, isDebug);
|
||||
EditorsConfig = new GridEditorsConfig(logger, appCaches, configFolder, manifestParser, isDebug);
|
||||
}
|
||||
|
||||
public IGridEditorsConfig EditorsConfig { get; }
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Manifest;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
@@ -15,13 +13,15 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
private readonly ILogger _logger;
|
||||
private readonly AppCaches _appCaches;
|
||||
private readonly DirectoryInfo _configFolder;
|
||||
private readonly ManifestParser _manifestParser;
|
||||
private readonly bool _isDebug;
|
||||
|
||||
public GridEditorsConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, bool isDebug)
|
||||
public GridEditorsConfig(ILogger logger, AppCaches appCaches, DirectoryInfo configFolder, ManifestParser manifestParser, bool isDebug)
|
||||
{
|
||||
_logger = logger;
|
||||
_appCaches = appCaches;
|
||||
_configFolder = configFolder;
|
||||
_manifestParser = manifestParser;
|
||||
_isDebug = isDebug;
|
||||
}
|
||||
|
||||
@@ -31,9 +31,6 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
{
|
||||
List<GridEditor> GetResult()
|
||||
{
|
||||
// TODO: should use the common one somehow! + ignoring _appPlugins here!
|
||||
var parser = new ManifestParser(_appCaches, Current.ManifestValidators, _logger);
|
||||
|
||||
var editors = new List<GridEditor>();
|
||||
var gridConfig = Path.Combine(_configFolder.FullName, "grid.editors.config.js");
|
||||
if (File.Exists(gridConfig))
|
||||
@@ -42,7 +39,7 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
|
||||
try
|
||||
{
|
||||
editors.AddRange(parser.ParseGridEditors(sourceString));
|
||||
editors.AddRange(_manifestParser.ParseGridEditors(sourceString));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -51,7 +48,7 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
}
|
||||
|
||||
// add manifest editors, skip duplicates
|
||||
foreach (var gridEditor in parser.Manifest.GridEditors)
|
||||
foreach (var gridEditor in _manifestParser.Manifest.GridEditors)
|
||||
{
|
||||
if (editors.Contains(gridEditor) == false) editors.Add(gridEditor);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace Umbraco.Core.Configuration.Grid
|
||||
public interface IGridEditorConfig
|
||||
{
|
||||
string Name { get; }
|
||||
string NameTemplate { get; }
|
||||
string Alias { get; }
|
||||
string View { get; }
|
||||
string Render { get; }
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class ContentElement : UmbracoConfigurationElement, IContentSection
|
||||
{
|
||||
private const string DefaultPreviewBadge = @"<a id=""umbracoPreviewBadge"" style=""z-index:99999; position: absolute; top: 0; right: 0; border: 0; width: 149px; height: 149px; background: url('{0}/assets/img/preview-mode-badge.png') no-repeat;"" href=""#"" OnClick=""javascript:window.top.location.href = '{0}/preview/end?redir={1}'""><span style=""display:none;"">In Preview Mode - click to end</span></a>";
|
||||
private const string DefaultPreviewBadge = @"<div id=""umbracoPreviewBadge"" class=""umbraco-preview-badge""><span class=""umbraco-preview-badge__header"">Preview mode</span><a href=""{0}/preview/end?redir={1}"" class=""umbraco-preview-badge__end""><svg viewBox=""0 0 100 100"" xmlns=""http://www.w3.org/2000/svg""><title>Click to end</title><path d=""M5273.1 2400.1v-2c0-2.8-5-4-9.7-4s-9.7 1.3-9.7 4v2a7 7 0 002 4.9l5 4.9c.3.3.4.6.4 1v6.4c0 .4.2.7.6.8l2.9.9c.5.1 1-.2 1-.8v-7.2c0-.4.2-.7.4-1l5.1-5a7 7 0 002-4.9zm-9.7-.1c-4.8 0-7.4-1.3-7.5-1.8.1-.5 2.7-1.8 7.5-1.8s7.3 1.3 7.5 1.8c-.2.5-2.7 1.8-7.5 1.8z""/><path d=""M5268.4 2410.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1s-.4-1-1-1h-4.3zM5272.7 2413.7h-4.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1s-.4-1-1-1zM5272.7 2417h-4.3c-.6 0-1 .4-1 1s.4 1 1 1h4.3c.6 0 1-.4 1-1 0-.5-.4-1-1-1z""/><path d=""M78.2 13l-8.7 11.7a32.5 32.5 0 11-51.9 25.8c0-10.3 4.7-19.7 12.9-25.8L21.8 13a47 47 0 1056.4 0z""/><path d=""M42.7 2.5h14.6v49.4H42.7z""/></svg></a></div><style type=""text/css"">.umbraco-preview-badge {{position: absolute;top: 1em;right: 1em;display: inline-flex;background: #1b264f;color: #fff;padding: 1em;font-size: 12px;z-index: 99999999;justify-content: center;align-items: center;box-shadow: 0 10px 50px rgba(0, 0, 0, .1), 0 6px 20px rgba(0, 0, 0, .16);line-height: 1;}}.umbraco-preview-badge__header {{font-weight: bold;}}.umbraco-preview-badge__end {{width: 3em;padding: 1em;margin: -1em -1em -1em 2em;display: flex;flex-shrink: 0;align-items: center;align-self: stretch;}}.umbraco-preview-badge__end:hover,.umbraco-preview-badge__end:focus {{background: #f5c1bc;}}.umbraco-preview-badge__end svg {{fill: #fff;width:1em;}}</style>";
|
||||
|
||||
[ConfigurationProperty("imaging")]
|
||||
internal ContentImagingElement Imaging => (ContentImagingElement) this["imaging"];
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
public interface IKeepAliveSection : IUmbracoConfigurationSection
|
||||
{
|
||||
bool DisableKeepAliveTask { get; }
|
||||
string KeepAlivePingUrl { get; }
|
||||
}
|
||||
}
|
||||
@@ -15,5 +15,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
ILoggingSection Logging { get; }
|
||||
|
||||
IWebRoutingSection WebRouting { get; }
|
||||
|
||||
IKeepAliveSection KeepAlive { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using System.Configuration;
|
||||
|
||||
namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
{
|
||||
internal class KeepAliveElement : ConfigurationElement, IKeepAliveSection
|
||||
{
|
||||
[ConfigurationProperty("disableKeepAliveTask", DefaultValue = "false")]
|
||||
public bool DisableKeepAliveTask => (bool)base["disableKeepAliveTask"];
|
||||
|
||||
[ConfigurationProperty("keepAlivePingUrl", DefaultValue = "{umbracoApplicationUrl}/api/keepalive/ping")]
|
||||
public string KeepAlivePingUrl => (string)base["keepAlivePingUrl"];
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,12 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
[ConfigurationProperty("logging")]
|
||||
internal LoggingElement Logging => (LoggingElement)this["logging"];
|
||||
|
||||
|
||||
[ConfigurationProperty("web.routing")]
|
||||
internal WebRoutingElement WebRouting => (WebRoutingElement)this["web.routing"];
|
||||
|
||||
[ConfigurationProperty("keepAlive")]
|
||||
internal KeepAliveElement KeepAlive => (KeepAliveElement)this["keepAlive"];
|
||||
|
||||
IContentSection IUmbracoSettingsSection.Content => Content;
|
||||
|
||||
ISecuritySection IUmbracoSettingsSection.Security => Security;
|
||||
@@ -34,5 +36,7 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
|
||||
ILoggingSection IUmbracoSettingsSection.Logging => Logging;
|
||||
|
||||
IWebRoutingSection IUmbracoSettingsSection.WebRouting => WebRouting;
|
||||
|
||||
IKeepAliveSection IUmbracoSettingsSection.KeepAlive => KeepAlive;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public static class AppSettings
|
||||
{
|
||||
public const string MainDomLock = "Umbraco.Core.MainDom.Lock";
|
||||
|
||||
// TODO: Kill me - still used in Umbraco.Core.IO.SystemFiles:27
|
||||
[Obsolete("We need to kill this appsetting as we do not use XML content cache umbraco.config anymore due to NuCache")]
|
||||
public const string ContentXML = "Umbraco.Core.ContentXML"; //umbracoContentXML
|
||||
@@ -41,6 +43,21 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string Path = "Umbraco.Core.Path";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the css directory (/css by default).
|
||||
/// </summary>
|
||||
public const string CssPath = "umbracoCssPath";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to the scripts directory (/scripts by default).
|
||||
/// </summary>
|
||||
public const string ScriptsPath = "umbracoScriptsPath";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path to media directory (/media by default).
|
||||
/// </summary>
|
||||
public const string MediaPath = "umbracoMediaPath";
|
||||
|
||||
/// <summary>
|
||||
/// The reserved urls from web.config.
|
||||
/// </summary>
|
||||
|
||||
@@ -221,7 +221,8 @@ namespace Umbraco.Core
|
||||
FailedPasswordAttempts,
|
||||
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Integer, true, FailedPasswordAttempts)
|
||||
{
|
||||
Name = FailedPasswordAttemptsLabel
|
||||
Name = FailedPasswordAttemptsLabel,
|
||||
DataTypeId = Constants.DataTypes.LabelInt
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -242,35 +243,40 @@ namespace Umbraco.Core
|
||||
LastLockoutDate,
|
||||
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate)
|
||||
{
|
||||
Name = LastLockoutDateLabel
|
||||
Name = LastLockoutDateLabel,
|
||||
DataTypeId = Constants.DataTypes.LabelDateTime
|
||||
}
|
||||
},
|
||||
{
|
||||
LastLoginDate,
|
||||
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate)
|
||||
{
|
||||
Name = LastLoginDateLabel
|
||||
Name = LastLoginDateLabel,
|
||||
DataTypeId = Constants.DataTypes.LabelDateTime
|
||||
}
|
||||
},
|
||||
{
|
||||
LastPasswordChangeDate,
|
||||
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate)
|
||||
{
|
||||
Name = LastPasswordChangeDateLabel
|
||||
Name = LastPasswordChangeDateLabel,
|
||||
DataTypeId = Constants.DataTypes.LabelDateTime
|
||||
}
|
||||
},
|
||||
{
|
||||
PasswordAnswer,
|
||||
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer)
|
||||
{
|
||||
Name = PasswordAnswerLabel
|
||||
Name = PasswordAnswerLabel,
|
||||
DataTypeId = Constants.DataTypes.LabelString
|
||||
}
|
||||
},
|
||||
{
|
||||
PasswordQuestion,
|
||||
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion)
|
||||
{
|
||||
Name = PasswordQuestionLabel
|
||||
Name = PasswordQuestionLabel,
|
||||
DataTypeId = Constants.DataTypes.LabelString
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -309,34 +315,65 @@ namespace Umbraco.Core
|
||||
public static class RelationTypes
|
||||
{
|
||||
/// <summary>
|
||||
/// ContentType name for default relation type "Relate Document On Copy".
|
||||
/// Name for default relation type "Related Media".
|
||||
/// </summary>
|
||||
public const string RelatedMediaName = "Related Media";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for default relation type "Related Media"
|
||||
/// </summary>
|
||||
public const string RelatedMediaAlias = "umbMedia";
|
||||
|
||||
/// <summary>
|
||||
/// Name for default relation type "Related Document".
|
||||
/// </summary>
|
||||
public const string RelatedDocumentName = "Related Document";
|
||||
|
||||
/// <summary>
|
||||
/// Alias for default relation type "Related Document"
|
||||
/// </summary>
|
||||
public const string RelatedDocumentAlias = "umbDocument";
|
||||
|
||||
/// <summary>
|
||||
/// Name for default relation type "Relate Document On Copy".
|
||||
/// </summary>
|
||||
public const string RelateDocumentOnCopyName = "Relate Document On Copy";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType alias for default relation type "Relate Document On Copy".
|
||||
/// Alias for default relation type "Relate Document On Copy".
|
||||
/// </summary>
|
||||
public const string RelateDocumentOnCopyAlias = "relateDocumentOnCopy";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType name for default relation type "Relate Parent Document On Delete".
|
||||
/// Name for default relation type "Relate Parent Document On Delete".
|
||||
/// </summary>
|
||||
public const string RelateParentDocumentOnDeleteName = "Relate Parent Document On Delete";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType alias for default relation type "Relate Parent Document On Delete".
|
||||
/// Alias for default relation type "Relate Parent Document On Delete".
|
||||
/// </summary>
|
||||
public const string RelateParentDocumentOnDeleteAlias = "relateParentDocumentOnDelete";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType name for default relation type "Relate Parent Media Folder On Delete".
|
||||
/// Name for default relation type "Relate Parent Media Folder On Delete".
|
||||
/// </summary>
|
||||
public const string RelateParentMediaFolderOnDeleteName = "Relate Parent Media Folder On Delete";
|
||||
|
||||
/// <summary>
|
||||
/// ContentType alias for default relation type "Relate Parent Media Folder On Delete".
|
||||
/// Alias for default relation type "Relate Parent Media Folder On Delete".
|
||||
/// </summary>
|
||||
public const string RelateParentMediaFolderOnDeleteAlias = "relateParentMediaFolderOnDelete";
|
||||
|
||||
/// <summary>
|
||||
/// Returns the types of relations that are automatically tracked
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Developers should not manually use these relation types since they will all be cleared whenever an entity
|
||||
/// (content, media or member) is saved since they are auto-populated based on property values.
|
||||
/// </remarks>
|
||||
public static string[] AutomaticRelationTypes = new[] { RelatedMediaAlias, RelatedDocumentAlias };
|
||||
|
||||
//TODO: return a list of built in types so we can use that to prevent deletion in the uI
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,375 @@
|
||||
namespace Umbraco.Core
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
public static class DataTypes
|
||||
{
|
||||
public const int LabelString = -92;
|
||||
//NOTE: unfortunately due to backwards compat we can't move/rename these, with the addition of the GUID
|
||||
//constants, it would make more sense to have these suffixed with "ID" or in a Subclass called "INT", for
|
||||
//now all we can do is make a subclass called Guids to put the GUID IDs.
|
||||
|
||||
public const int LabelString = System.DefaultLabelDataTypeId;
|
||||
public const int LabelInt = -91;
|
||||
public const int LabelBigint = -93;
|
||||
public const int LabelDateTime = -94;
|
||||
public const int LabelTime = -98;
|
||||
public const int LabelDecimal = -99;
|
||||
|
||||
public const int Textarea = -89;
|
||||
public const int Textbox = -88;
|
||||
public const int RichtextEditor = -87;
|
||||
public const int Boolean = -49;
|
||||
public const int DateTime = -36;
|
||||
public const int DropDownSingle = -39;
|
||||
public const int DropDownMultiple = -42;
|
||||
public const int Upload = -90;
|
||||
|
||||
public const int DefaultContentListView = -95;
|
||||
public const int DefaultMediaListView = -96;
|
||||
public const int DefaultMembersListView = -97;
|
||||
|
||||
public const int ImageCropper = 1043;
|
||||
public const int Tags = 1041;
|
||||
|
||||
public static class ReservedPreValueKeys
|
||||
{
|
||||
public const string IgnoreUserStartNodes = "ignoreUserStartNodes";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the identifiers for Umbraco data types as constants for easy centralized access/management.
|
||||
/// </summary>
|
||||
public static class Guids
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Content Picker as string
|
||||
/// </summary>
|
||||
public const string ContentPicker = "FD1E0DA5-5606-4862-B679-5D0CF3A52A59";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Content Picker
|
||||
/// </summary>
|
||||
public static readonly Guid ContentPickerGuid = new Guid(ContentPicker);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Member Picker as string
|
||||
/// </summary>
|
||||
public const string MemberPicker = "1EA2E01F-EBD8-4CE1-8D71-6B1149E63548";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Member Picker
|
||||
/// </summary>
|
||||
public static readonly Guid MemberPickerGuid = new Guid(MemberPicker);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Media Picker as string
|
||||
/// </summary>
|
||||
public const string MediaPicker = "135D60E0-64D9-49ED-AB08-893C9BA44AE5";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Media Picker
|
||||
/// </summary>
|
||||
public static readonly Guid MediaPickerGuid = new Guid(MediaPicker);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Multiple Media Picker as string
|
||||
/// </summary>
|
||||
public const string MultipleMediaPicker = "9DBBCBBB-2327-434A-B355-AF1B84E5010A";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Multiple Media Picker
|
||||
/// </summary>
|
||||
public static readonly Guid MultipleMediaPickerGuid = new Guid(MultipleMediaPicker);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Related Links as string
|
||||
/// </summary>
|
||||
public const string RelatedLinks = "B4E3535A-1753-47E2-8568-602CF8CFEE6F";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Related Links
|
||||
/// </summary>
|
||||
public static readonly Guid RelatedLinksGuid = new Guid(RelatedLinks);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Member as string
|
||||
/// </summary>
|
||||
public const string Member = "d59be02f-1df9-4228-aa1e-01917d806cda";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Member
|
||||
/// </summary>
|
||||
public static readonly Guid MemberGuid = new Guid(Member);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Image Cropper as string
|
||||
/// </summary>
|
||||
public const string ImageCropper = "1df9f033-e6d4-451f-b8d2-e0cbc50a836f";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Image Cropper
|
||||
/// </summary>
|
||||
public static readonly Guid ImageCropperGuid = new Guid(ImageCropper);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Tags as string
|
||||
/// </summary>
|
||||
public const string Tags = "b6b73142-b9c1-4bf8-a16d-e1c23320b549";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Tags
|
||||
/// </summary>
|
||||
public static readonly Guid TagsGuid = new Guid(Tags);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for List View - Content as string
|
||||
/// </summary>
|
||||
public const string ListViewContent = "C0808DD3-8133-4E4B-8CE8-E2BEA84A96A4";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for List View - Content
|
||||
/// </summary>
|
||||
public static readonly Guid ListViewContentGuid = new Guid(ListViewContent);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for List View - Media as string
|
||||
/// </summary>
|
||||
public const string ListViewMedia = "3A0156C4-3B8C-4803-BDC1-6871FAA83FFF";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for List View - Media
|
||||
/// </summary>
|
||||
public static readonly Guid ListViewMediaGuid = new Guid(ListViewMedia);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for List View - Members as string
|
||||
/// </summary>
|
||||
public const string ListViewMembers = "AA2C52A0-CE87-4E65-A47C-7DF09358585D";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for List View - Members
|
||||
/// </summary>
|
||||
public static readonly Guid ListViewMembersGuid = new Guid(ListViewMembers);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Date Picker with time as string
|
||||
/// </summary>
|
||||
public const string DatePickerWithTime = "e4d66c0f-b935-4200-81f0-025f7256b89a";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Date Picker with time
|
||||
/// </summary>
|
||||
public static readonly Guid DatePickerWithTimeGuid = new Guid(DatePickerWithTime);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Approved Color as string
|
||||
/// </summary>
|
||||
public const string ApprovedColor = "0225af17-b302-49cb-9176-b9f35cab9c17";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Approved Color
|
||||
/// </summary>
|
||||
public static readonly Guid ApprovedColorGuid = new Guid(ApprovedColor);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Dropdown multiple as string
|
||||
/// </summary>
|
||||
public const string DropdownMultiple = "f38f0ac7-1d27-439c-9f3f-089cd8825a53";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Dropdown multiple
|
||||
/// </summary>
|
||||
public static readonly Guid DropdownMultipleGuid = new Guid(DropdownMultiple);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Radiobox as string
|
||||
/// </summary>
|
||||
public const string Radiobox = "bb5f57c9-ce2b-4bb9-b697-4caca783a805";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Radiobox
|
||||
/// </summary>
|
||||
public static readonly Guid RadioboxGuid = new Guid(Radiobox);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Date Picker as string
|
||||
/// </summary>
|
||||
public const string DatePicker = "5046194e-4237-453c-a547-15db3a07c4e1";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Date Picker
|
||||
/// </summary>
|
||||
public static readonly Guid DatePickerGuid = new Guid(DatePicker);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Dropdown as string
|
||||
/// </summary>
|
||||
public const string Dropdown = "0b6a45e7-44ba-430d-9da5-4e46060b9e03";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Dropdown
|
||||
/// </summary>
|
||||
public static readonly Guid DropdownGuid = new Guid(Dropdown);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Checkbox list as string
|
||||
/// </summary>
|
||||
public const string CheckboxList = "fbaf13a8-4036-41f2-93a3-974f678c312a";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Checkbox list
|
||||
/// </summary>
|
||||
public static readonly Guid CheckboxListGuid = new Guid(CheckboxList);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Checkbox as string
|
||||
/// </summary>
|
||||
public const string Checkbox = "92897bc6-a5f3-4ffe-ae27-f2e7e33dda49";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Checkbox
|
||||
/// </summary>
|
||||
public static readonly Guid CheckboxGuid = new Guid(Checkbox);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Numeric as string
|
||||
/// </summary>
|
||||
public const string Numeric = "2e6d3631-066e-44b8-aec4-96f09099b2b5";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Dropdown
|
||||
/// </summary>
|
||||
public static readonly Guid NumericGuid = new Guid(Numeric);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Richtext editor as string
|
||||
/// </summary>
|
||||
public const string RichtextEditor = "ca90c950-0aff-4e72-b976-a30b1ac57dad";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Richtext editor
|
||||
/// </summary>
|
||||
public static readonly Guid RichtextEditorGuid = new Guid(RichtextEditor);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Textstring as string
|
||||
/// </summary>
|
||||
public const string Textstring = "0cc0eba1-9960-42c9-bf9b-60e150b429ae";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Textstring
|
||||
/// </summary>
|
||||
public static readonly Guid TextstringGuid = new Guid(Textstring);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Textarea as string
|
||||
/// </summary>
|
||||
public const string Textarea = "c6bac0dd-4ab9-45b1-8e30-e4b619ee5da3";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Dropdown
|
||||
/// </summary>
|
||||
public static readonly Guid TextareaGuid = new Guid(Textarea);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Upload as string
|
||||
/// </summary>
|
||||
public const string Upload = "84c6b441-31df-4ffe-b67e-67d5bc3ae65a";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Upload
|
||||
/// </summary>
|
||||
public static readonly Guid UploadGuid = new Guid(Upload);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label as string
|
||||
/// </summary>
|
||||
public const string LabelString = "f0bc4bfb-b499-40d6-ba86-058885a5178c";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label string
|
||||
/// </summary>
|
||||
public static readonly Guid LabelStringGuid = new Guid(LabelString);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label as int
|
||||
/// </summary>
|
||||
public const string LabelInt = "8e7f995c-bd81-4627-9932-c40e568ec788";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label int
|
||||
/// </summary>
|
||||
public static readonly Guid LabelIntGuid = new Guid(LabelInt);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label as big int
|
||||
/// </summary>
|
||||
public const string LabelBigInt = "930861bf-e262-4ead-a704-f99453565708";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label big int
|
||||
/// </summary>
|
||||
public static readonly Guid LabelBigIntGuid = new Guid(LabelBigInt);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label as date time
|
||||
/// </summary>
|
||||
public const string LabelDateTime = "0e9794eb-f9b5-4f20-a788-93acd233a7e4";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label date time
|
||||
/// </summary>
|
||||
public static readonly Guid LabelDateTimeGuid = new Guid(LabelDateTime);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label as time
|
||||
/// </summary>
|
||||
public const string LabelTime = "a97cec69-9b71-4c30-8b12-ec398860d7e8";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label time
|
||||
/// </summary>
|
||||
public static readonly Guid LabelTimeGuid = new Guid(LabelTime);
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label as decimal
|
||||
/// </summary>
|
||||
public const string LabelDecimal = "8f1ef1e1-9de4-40d3-a072-6673f631ca64";
|
||||
|
||||
/// <summary>
|
||||
/// Guid for Label decimal
|
||||
/// </summary>
|
||||
public static readonly Guid LabelDecimalGuid = new Guid(LabelDecimal);
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,39 +5,89 @@
|
||||
public static class Icons
|
||||
{
|
||||
/// <summary>
|
||||
/// System contenttype icon
|
||||
/// System default icon
|
||||
/// </summary>
|
||||
public const string ContentType = "icon-arrangement";
|
||||
public const string DefaultIcon = Content;
|
||||
|
||||
/// <summary>
|
||||
/// System datatype icon
|
||||
/// System content icon
|
||||
/// </summary>
|
||||
public const string Content = "icon-document";
|
||||
|
||||
/// <summary>
|
||||
/// System content type icon
|
||||
/// </summary>
|
||||
public const string ContentType = "icon-item-arrangement";
|
||||
|
||||
/// <summary>
|
||||
/// System data type icon
|
||||
/// </summary>
|
||||
public const string DataType = "icon-autofill";
|
||||
|
||||
/// <summary>
|
||||
/// System property editor icon
|
||||
/// System list view icon
|
||||
/// </summary>
|
||||
public const string PropertyEditor = "icon-autofill";
|
||||
public const string ListView = "icon-thumbnail-list";
|
||||
|
||||
/// <summary>
|
||||
/// System macro icon
|
||||
/// </summary>
|
||||
public const string Macro = "icon-settings-alt";
|
||||
|
||||
/// <summary>
|
||||
/// System media file icon
|
||||
/// </summary>
|
||||
public const string MediaFile = "icon-document";
|
||||
|
||||
/// <summary>
|
||||
/// System media folder icon
|
||||
/// </summary>
|
||||
public const string MediaFolder = "icon-folder";
|
||||
|
||||
/// <summary>
|
||||
/// System media image icon
|
||||
/// </summary>
|
||||
public const string MediaImage = "icon-picture";
|
||||
|
||||
/// <summary>
|
||||
/// System media type icon
|
||||
/// </summary>
|
||||
public const string MediaType = "icon-thumbnails";
|
||||
|
||||
/// <summary>
|
||||
/// System member icon
|
||||
/// </summary>
|
||||
public const string Member = "icon-user";
|
||||
|
||||
/// <summary>
|
||||
/// System member icon
|
||||
/// System member group icon
|
||||
/// </summary>
|
||||
public const string MemberGroup = "icon-users-alt";
|
||||
|
||||
/// <summary>
|
||||
/// System member type icon
|
||||
/// </summary>
|
||||
public const string MemberType = "icon-users";
|
||||
|
||||
/// <summary>
|
||||
/// System property editor icon
|
||||
/// </summary>
|
||||
public const string PropertyEditor = "icon-autofill";
|
||||
|
||||
/// <summary>
|
||||
/// System member icon
|
||||
/// </summary>
|
||||
public const string Template = "icon-layout";
|
||||
|
||||
/// <summary>
|
||||
/// System user icon
|
||||
/// </summary>
|
||||
public const string User = "icon-user";
|
||||
|
||||
/// <summary>
|
||||
/// System user group icon
|
||||
/// </summary>
|
||||
public const string UserGroup = "icon-users";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,23 @@ namespace Umbraco.Core
|
||||
/// </summary>
|
||||
public const string InternalGenericPropertiesPrefix = "_umb_";
|
||||
|
||||
public static class Legacy
|
||||
{
|
||||
public static class Aliases
|
||||
{
|
||||
public const string Textbox = "Umbraco.Textbox";
|
||||
public const string Date = "Umbraco.Date";
|
||||
public const string ContentPicker2 = "Umbraco.ContentPicker2";
|
||||
public const string MediaPicker2 = "Umbraco.MediaPicker2";
|
||||
public const string MemberPicker2 = "Umbraco.MemberPicker2";
|
||||
public const string MultiNodeTreePicker2 = "Umbraco.MultiNodeTreePicker2";
|
||||
public const string TextboxMultiple = "Umbraco.TextboxMultiple";
|
||||
public const string RelatedLinks2 = "Umbraco.RelatedLinks2";
|
||||
public const string RelatedLinks = "Umbraco.RelatedLinks";
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines Umbraco built-in property editor aliases.
|
||||
/// </summary>
|
||||
@@ -68,17 +85,17 @@ namespace Umbraco.Core
|
||||
/// ListView.
|
||||
/// </summary>
|
||||
public const string ListView = "Umbraco.ListView";
|
||||
|
||||
/// <summary>
|
||||
/// Macro Container.
|
||||
/// </summary>
|
||||
public const string MacroContainer = "Umbraco.MacroContainer";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Media Picker.
|
||||
/// </summary>
|
||||
public const string MediaPicker = "Umbraco.MediaPicker";
|
||||
|
||||
/// <summary>
|
||||
/// Multiple Media Picker.
|
||||
/// </summary>
|
||||
public const string MultipleMediaPicker = "Umbraco.MultipleMediaPicker";
|
||||
|
||||
/// <summary>
|
||||
/// Member Picker.
|
||||
/// </summary>
|
||||
@@ -186,6 +203,24 @@ namespace Umbraco.Core
|
||||
/// <remarks>Must be a valid <see cref="ValueTypes"/> value.</remarks>
|
||||
public const string DataValueType = "umbracoDataValueType";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines Umbraco's built-in property editor groups.
|
||||
/// </summary>
|
||||
public static class Groups
|
||||
{
|
||||
public const string Common = "Common";
|
||||
|
||||
public const string Lists = "Lists";
|
||||
|
||||
public const string Media = "Media";
|
||||
|
||||
public const string People = "People";
|
||||
|
||||
public const string Pickers = "Pickers";
|
||||
|
||||
public const string RichContent = "Rich Content";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,16 +15,23 @@ namespace Umbraco.Core
|
||||
public const int SuperUserId = -1;
|
||||
|
||||
/// <summary>
|
||||
/// The id for the 'unknown' user
|
||||
/// The id for the 'unknown' user.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This is a user row that exists in the DB only for referential integrity but the user is never returned from any of the services
|
||||
/// </remarks>
|
||||
public const int UnknownUserId = 0;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the 'unknown' user.
|
||||
/// </summary>
|
||||
public const string UnknownUserName = "SYTEM";
|
||||
|
||||
public const string AdminGroupAlias = "admin";
|
||||
public const string EditorGroupAlias = "editor";
|
||||
public const string SensitiveDataGroupAlias = "sensitiveData";
|
||||
public const string TranslatorGroupAlias = "translator";
|
||||
public const string WriterGroupAlias = "writer";
|
||||
|
||||
public const string BackOfficeAuthenticationType = "UmbracoBackOffice";
|
||||
public const string BackOfficeExternalAuthenticationType = "UmbracoExternalCookie";
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
public static partial class Constants
|
||||
{
|
||||
public static class SqlTemplates
|
||||
{
|
||||
public static class VersionableRepository
|
||||
{
|
||||
public const string GetVersionIds = "Umbraco.Core.VersionableRepository.GetVersionIds";
|
||||
public const string GetVersion = "Umbraco.Core.VersionableRepository.GetVersion";
|
||||
public const string GetVersions = "Umbraco.Core.VersionableRepository.GetVersions";
|
||||
public const string EnsureUniqueNodeName = "Umbraco.Core.VersionableRepository.EnsureUniqueNodeName";
|
||||
public const string GetSortOrder = "Umbraco.Core.VersionableRepository.GetSortOrder";
|
||||
public const string GetParentNode = "Umbraco.Core.VersionableRepository.GetParentNode";
|
||||
public const string GetReservedId = "Umbraco.Core.VersionableRepository.GetReservedId";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,7 @@ using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Models.Entities;
|
||||
using Umbraco.Core.Models.Membership;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
using Umbraco.Core.Services;
|
||||
using Umbraco.Core.Services.Implement;
|
||||
|
||||
@@ -23,6 +24,8 @@ namespace Umbraco.Core
|
||||
// this ain't pretty
|
||||
private static IMediaFileSystem _mediaFileSystem;
|
||||
private static IMediaFileSystem MediaFileSystem => _mediaFileSystem ?? (_mediaFileSystem = Current.MediaFileSystem);
|
||||
private static readonly PropertyEditorCollection _propertyEditors;
|
||||
private static PropertyEditorCollection PropertyEditors = _propertyEditors ?? (_propertyEditors = Current.PropertyEditors);
|
||||
|
||||
#region IContent
|
||||
|
||||
@@ -57,6 +60,19 @@ namespace Umbraco.Core
|
||||
|
||||
#endregion
|
||||
|
||||
internal static bool IsMoving(this IContentBase entity)
|
||||
{
|
||||
// Check if this entity is being moved as a descendant as part of a bulk moving operations.
|
||||
// When this occurs, only Path + Level + UpdateDate are being changed. In this case we can bypass a lot of the below
|
||||
// operations which will make this whole operation go much faster. When moving we don't need to create
|
||||
// new versions, etc... because we cannot roll this operation back anyways.
|
||||
var isMoving = entity.IsPropertyDirty(nameof(entity.Path))
|
||||
&& entity.IsPropertyDirty(nameof(entity.Level))
|
||||
&& entity.IsPropertyDirty(nameof(entity.UpdateDate));
|
||||
|
||||
return isMoving;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes characters that are not valid XML characters from all entity properties
|
||||
/// of type string. See: http://stackoverflow.com/a/961504/5018
|
||||
@@ -162,14 +178,12 @@ namespace Umbraco.Core
|
||||
// Fixes https://github.com/umbraco/Umbraco-CMS/issues/3937 - Assigning a new file to an
|
||||
// existing IMedia with extension SetValue causes exception 'Illegal characters in path'
|
||||
string oldpath = null;
|
||||
if (property.GetValue(culture, segment) is string svalue)
|
||||
var value = property.GetValue(culture, segment);
|
||||
|
||||
if (PropertyEditors.TryGet(propertyTypeAlias, out var editor)
|
||||
&& editor is IDataEditorWithMediaPath dataEditor)
|
||||
{
|
||||
if (svalue.DetectIsJson())
|
||||
{
|
||||
// the property value is a JSON serialized image crop data set - grab the "src" property as the file source
|
||||
var jObject = JsonConvert.DeserializeObject<JObject>(svalue);
|
||||
svalue = jObject != null ? jObject.GetValueAsString("src") : svalue;
|
||||
}
|
||||
var svalue = dataEditor.GetMediaPath(value);
|
||||
oldpath = MediaFileSystem.GetRelativePath(svalue);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,121 +12,260 @@ namespace Umbraco.Core
|
||||
/// <summary>
|
||||
/// Determines whether the content type is invariant.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this ISimpleContentType contentType) => contentType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
public static bool VariesByCulture(this ISimpleContentType contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type is invariant.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this IContentTypeBase contentType) => contentType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// Determines whether the content type is invariant.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by segment.</remarks>
|
||||
public static bool VariesByCulture(this IContentTypeBase contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by culture.</remarks>
|
||||
public static bool VariesBySegment(this IContentTypeBase contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
public static bool VariesByCultureAndSegment(this IContentTypeBase contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this IPublishedContentType contentType) => contentType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type is invariant.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this PropertyType propertyType) => propertyType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by segment.</remarks>
|
||||
public static bool VariesByCulture(this PropertyType propertyType) => propertyType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by segment.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by culture.</remarks>
|
||||
public static bool VariesBySegment(this PropertyType propertyType) => propertyType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture and segment.
|
||||
/// </summary>
|
||||
public static bool VariesByCultureAndSegment(this PropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type is invariant.
|
||||
/// </summary>
|
||||
public static bool VariesByNothing(this PublishedContentType contentType) => contentType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by segment.</remarks>
|
||||
public static bool VariesByCulture(this PublishedContentType contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by culture.</remarks>
|
||||
public static bool VariesBySegment(this PublishedContentType contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
public static bool VariesByCultureAndSegment(this PublishedContentType contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type is invariant.
|
||||
/// </summary>
|
||||
public static bool VariesByNothing(this PublishedPropertyType propertyType) => propertyType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture.
|
||||
/// </summary>
|
||||
public static bool VariesByCulture(this PublishedPropertyType propertyType) => propertyType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by segment.
|
||||
/// </summary>
|
||||
public static bool VariesBySegment(this PublishedPropertyType propertyType) => propertyType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture and segment.
|
||||
/// </summary>
|
||||
public static bool VariesByCultureAndSegment(this PublishedPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment();
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByNothing();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a variation is invariant.
|
||||
/// </summary>
|
||||
/// <param name="variation">The variation.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the variation is invariant.
|
||||
/// </returns>
|
||||
public static bool VariesByNothing(this ContentVariation variation) => variation == ContentVariation.Nothing;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this ISimpleContentType contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this IContentTypeBase contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this IPublishedContentType contentType) => contentType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this PropertyType propertyType) => propertyType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a variation varies by culture.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by segment.</remarks>
|
||||
/// <param name="variation">The variation.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the variation varies by culture.
|
||||
/// </returns>
|
||||
public static bool VariesByCulture(this ContentVariation variation) => (variation & ContentVariation.Culture) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this ISimpleContentType contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this IContentTypeBase contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this IPublishedContentType contentType) => contentType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this PropertyType propertyType) => propertyType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by segment.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesBySegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a variation varies by segment.
|
||||
/// </summary>
|
||||
/// <remarks>And then it could also vary by culture.</remarks>
|
||||
/// <param name="variation">The variation.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the variation varies by segment.
|
||||
/// </returns>
|
||||
public static bool VariesBySegment(this ContentVariation variation) => (variation & ContentVariation.Segment) > 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this ISimpleContentType contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this IContentTypeBase contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the content type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the content type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this IPublishedContentType contentType) => contentType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this PropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the property type varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the property type varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this IPublishedPropertyType propertyType) => propertyType.Variations.VariesByCultureAndSegment();
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a variation varies by culture and segment.
|
||||
/// </summary>
|
||||
/// <param name="variation">The variation.</param>
|
||||
/// <returns>
|
||||
/// A value indicating whether the variation varies by culture and segment.
|
||||
/// </returns>
|
||||
public static bool VariesByCultureAndSegment(this ContentVariation variation) => (variation & ContentVariation.CultureAndSegment) == ContentVariation.CultureAndSegment;
|
||||
|
||||
/// <summary>
|
||||
/// Sets or removes the content type variation depending on the specified value.
|
||||
/// </summary>
|
||||
/// <param name="contentType">The content type.</param>
|
||||
/// <param name="variation">The variation to set or remove.</param>
|
||||
/// <param name="value">If set to <c>true</c> sets the variation; otherwise, removes the variation.</param>
|
||||
/// <remarks>
|
||||
/// This method does not support setting the variation to nothing.
|
||||
/// </remarks>
|
||||
public static void SetVariesBy(this IContentTypeBase contentType, ContentVariation variation, bool value = true) => contentType.Variations = contentType.Variations.SetFlag(variation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets or removes the property type variation depending on the specified value.
|
||||
/// </summary>
|
||||
/// <param name="propertyType">The property type.</param>
|
||||
/// <param name="variation">The variation to set or remove.</param>
|
||||
/// <param name="value">If set to <c>true</c> sets the variation; otherwise, removes the variation.</param>
|
||||
/// <remarks>
|
||||
/// This method does not support setting the variation to nothing.
|
||||
/// </remarks>
|
||||
public static void SetVariesBy(this PropertyType propertyType, ContentVariation variation, bool value = true) => propertyType.Variations = propertyType.Variations.SetFlag(variation, value);
|
||||
|
||||
/// <summary>
|
||||
/// Returns the variations with the variation set or removed depending on the specified value.
|
||||
/// </summary>
|
||||
/// <param name="variations">The existing variations.</param>
|
||||
/// <param name="variation">The variation to set or remove.</param>
|
||||
/// <param name="value">If set to <c>true</c> sets the variation; otherwise, removes the variation.</param>
|
||||
/// <returns>
|
||||
/// The variations with the variation set or removed.
|
||||
/// </returns>
|
||||
/// <remarks>
|
||||
/// This method does not support setting the variation to nothing.
|
||||
/// </remarks>
|
||||
public static ContentVariation SetFlag(this ContentVariation variations, ContentVariation variation, bool value = true)
|
||||
{
|
||||
return value
|
||||
? variations | variation // Set flag using bitwise logical OR
|
||||
: variations & ~variation; // Remove flag using bitwise logical AND with bitwise complement (reversing the bit)
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Validates that a combination of culture and segment is valid for the variation.
|
||||
/// </summary>
|
||||
@@ -135,54 +274,66 @@ namespace Umbraco.Core
|
||||
/// <param name="segment">The segment.</param>
|
||||
/// <param name="exact">A value indicating whether to perform exact validation.</param>
|
||||
/// <param name="wildcards">A value indicating whether to support wildcards.</param>
|
||||
/// <param name="throwIfInvalid">A value indicating whether to throw a <see cref="NotSupportedException"/> when the combination is invalid.</param>
|
||||
/// <returns>True if the combination is valid; otherwise false.</returns>
|
||||
/// <param name="throwIfInvalid">A value indicating whether to throw a <see cref="NotSupportedException" /> when the combination is invalid.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if the combination is valid; otherwise <c>false</c>.
|
||||
/// </returns>
|
||||
/// <exception cref="NotSupportedException">Occurs when the combination is invalid, and <paramref name="throwIfInvalid" /> is true.</exception>
|
||||
/// <remarks>
|
||||
/// <para>When validation is exact, the combination must match the variation exactly. For instance, if the variation is Culture, then
|
||||
/// a culture is required. When validation is not strict, the combination must be equivalent, or more restrictive: if the variation is
|
||||
/// Culture, an invariant combination is ok.</para>
|
||||
/// <para>Basically, exact is for one content type, or one property type, and !exact is for "all property types" of one content type.</para>
|
||||
/// <para>Both <paramref name="culture"/> and <paramref name="segment"/> can be "*" to indicate "all of them".</para>
|
||||
/// <para>Both <paramref name="culture" /> and <paramref name="segment" /> can be "*" to indicate "all of them".</para>
|
||||
/// </remarks>
|
||||
/// <exception cref="NotSupportedException">Occurs when the combination is invalid, and <paramref name="throwIfInvalid"/> is true.</exception>
|
||||
public static bool ValidateVariation(this ContentVariation variation, string culture, string segment, bool exact, bool wildcards, bool throwIfInvalid)
|
||||
{
|
||||
culture = culture.NullOrWhiteSpaceAsNull();
|
||||
segment = segment.NullOrWhiteSpaceAsNull();
|
||||
|
||||
bool Validate(bool variesBy, string value)
|
||||
{
|
||||
if (variesBy)
|
||||
{
|
||||
// varies by
|
||||
// in exact mode, the value cannot be null (but it can be a wildcard)
|
||||
// in !wildcards mode, the value cannot be a wildcard (but it can be null)
|
||||
if ((exact && value == null) || (!wildcards && value == "*"))
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// does not vary by value
|
||||
// the value cannot have a value
|
||||
// unless wildcards and it's "*"
|
||||
if (value != null && (!wildcards || value != "*"))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!Validate(variation.VariesByCulture(), culture))
|
||||
// if wildcards are disabled, do not allow "*"
|
||||
if (!wildcards && (culture == "*" || segment == "*"))
|
||||
{
|
||||
if (throwIfInvalid)
|
||||
throw new NotSupportedException($"Culture value \"{culture ?? "<null>"}\" is invalid.");
|
||||
throw new NotSupportedException($"Variation wildcards are not supported.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Validate(variation.VariesBySegment(), segment))
|
||||
if (variation.VariesByCulture())
|
||||
{
|
||||
// varies by culture
|
||||
// in exact mode, the culture cannot be null
|
||||
if (exact && culture == null)
|
||||
{
|
||||
if (throwIfInvalid)
|
||||
throw new NotSupportedException($"Culture may not be null because culture variation is enabled.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// does not vary by culture
|
||||
// the culture cannot have a value
|
||||
// unless wildcards and it's "*"
|
||||
if (culture != null && !(wildcards && culture == "*"))
|
||||
{
|
||||
if (throwIfInvalid)
|
||||
throw new NotSupportedException($"Culture \"{culture}\" is invalid because culture variation is disabled.");
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// if it does not vary by segment
|
||||
// the segment cannot have a value
|
||||
// segment may always be null, even when the ContentVariation.Segment flag is set for this variation,
|
||||
// therefore the exact parameter is not used in segment validation.
|
||||
if (!variation.VariesBySegment() && segment != null && !(wildcards && segment == "*"))
|
||||
{
|
||||
if (throwIfInvalid)
|
||||
throw new NotSupportedException($"Segment value \"{segment ?? "<null>"}\" is invalid.");
|
||||
throw new NotSupportedException($"Segment \"{segment}\" is invalid because segment variation is disabled.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
@@ -15,7 +16,7 @@ namespace Umbraco.Core
|
||||
/// <returns></returns>
|
||||
public static string ToIsoString(this DateTime dt)
|
||||
{
|
||||
return dt.ToString("yyyy-MM-dd HH:mm:ss");
|
||||
return dt.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
public static DateTime TruncateTo(this DateTime dt, DateTruncate truncateTo)
|
||||
|
||||
@@ -3,43 +3,42 @@
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides extension methods to enums.
|
||||
/// Provides extension methods to <see cref="Enum"/>.
|
||||
/// </summary>
|
||||
public static class EnumExtensions
|
||||
{
|
||||
// note:
|
||||
// - no need to HasFlagExact, that's basically an == test
|
||||
// - HasFlagAll cannot be named HasFlag because ext. methods never take priority over instance methods
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a flag enum has all the specified values.
|
||||
/// Determines whether all the flags/bits are set within the enum value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>True when all bits set in <paramref name="uses"/> are set in <paramref name="use"/>, though other bits may be set too.</para>
|
||||
/// <para>This is the behavior of the original <see cref="Enum.HasFlag"/> method.</para>
|
||||
/// </remarks>
|
||||
public static bool HasFlagAll<T>(this T use, T uses)
|
||||
/// <typeparam name="T">The enum type.</typeparam>
|
||||
/// <param name="value">The enum value.</param>
|
||||
/// <param name="flags">The flags.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if all the flags/bits are set within the enum value; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
[Obsolete("Use Enum.HasFlag() or bitwise operations (if performance is important) instead.")]
|
||||
public static bool HasFlagAll<T>(this T value, T flags)
|
||||
where T : Enum
|
||||
{
|
||||
var num = Convert.ToUInt64(use);
|
||||
var nums = Convert.ToUInt64(uses);
|
||||
|
||||
return (num & nums) == nums;
|
||||
return value.HasFlag(flags);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether a flag enum has any of the specified values.
|
||||
/// Determines whether any of the flags/bits are set within the enum value.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>True when at least one of the bits set in <paramref name="uses"/> is set in <paramref name="use"/>.</para>
|
||||
/// </remarks>
|
||||
public static bool HasFlagAny<T>(this T use, T uses)
|
||||
/// <typeparam name="T">The enum type.</typeparam>
|
||||
/// <param name="value">The value.</param>
|
||||
/// <param name="flags">The flags.</param>
|
||||
/// <returns>
|
||||
/// <c>true</c> if any of the flags/bits are set within the enum value; otherwise, <c>false</c>.
|
||||
/// </returns>
|
||||
public static bool HasFlagAny<T>(this T value, T flags)
|
||||
where T : Enum
|
||||
{
|
||||
var num = Convert.ToUInt64(use);
|
||||
var nums = Convert.ToUInt64(uses);
|
||||
var v = Convert.ToUInt64(value);
|
||||
var f = Convert.ToUInt64(flags);
|
||||
|
||||
return (num & nums) > 0;
|
||||
return (v & f) > 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,8 @@ namespace Umbraco.Core
|
||||
///</summary>
|
||||
public static class EnumerableExtensions
|
||||
{
|
||||
internal static bool IsCollectionEmpty<T>(this IReadOnlyCollection<T> list) => list == null || list.Count == 0;
|
||||
|
||||
internal static bool HasDuplicates<T>(this IEnumerable<T> items, bool includeNull)
|
||||
{
|
||||
var hs = new HashSet<T>();
|
||||
@@ -112,7 +114,6 @@ namespace Umbraco.Core
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if all items in the other collection exist in this collection
|
||||
/// </summary>
|
||||
|
||||
@@ -3,7 +3,7 @@ using Umbraco.Core.Models.Membership;
|
||||
|
||||
namespace Umbraco.Core.Events
|
||||
{
|
||||
internal class UserGroupWithUsers
|
||||
public class UserGroupWithUsers
|
||||
{
|
||||
public UserGroupWithUsers(IUserGroup userGroup, IUser[] addedUsers, IUser[] removedUsers)
|
||||
{
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when a null reference, or an empty argument,
|
||||
/// is passed to a method that does not accept it as a valid argument.
|
||||
/// The exception that is thrown when a null reference, or an empty argument, is passed to a method that does not accept it as a valid argument.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.ArgumentNullException" />
|
||||
[Obsolete("Throw an ArgumentNullException when the parameter is null or an ArgumentException when its empty instead.")]
|
||||
[Serializable]
|
||||
public class ArgumentNullOrEmptyException : ArgumentNullException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException"/> class
|
||||
/// with the name of the parameter that caused this exception.
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
|
||||
/// </summary>
|
||||
public ArgumentNullOrEmptyException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class with the name of the parameter that caused this exception.
|
||||
/// </summary>
|
||||
/// <param name="paramName">The named of the parameter that caused the exception.</param>
|
||||
public ArgumentNullOrEmptyException(string paramName)
|
||||
@@ -18,13 +26,30 @@ namespace Umbraco.Core.Exceptions
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException"/> class
|
||||
/// with a specified error message and the name of the parameter that caused this exception.
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class with a specified error message and the name of the parameter that caused this exception.
|
||||
/// </summary>
|
||||
/// <param name="paramName">The named of the parameter that caused the exception.</param>
|
||||
/// <param name="message">A message that describes the error.</param>
|
||||
public ArgumentNullOrEmptyException(string paramName, string message)
|
||||
: base(paramName, message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for this exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public ArgumentNullOrEmptyException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ArgumentNullOrEmptyException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The object that holds the serialized object data.</param>
|
||||
/// <param name="context">An object that describes the source or destination of the serialized data.</param>
|
||||
protected ArgumentNullOrEmptyException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,45 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when authorization failed.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class AuthorizationException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthorizationException" /> class.
|
||||
/// </summary>
|
||||
public AuthorizationException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthorizationException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public AuthorizationException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthorizationException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public AuthorizationException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AuthorizationException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected AuthorizationException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
@@ -6,6 +7,8 @@ namespace Umbraco.Core.Exceptions
|
||||
/// <summary>
|
||||
/// An exception that is thrown if the Umbraco application cannot boot.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class BootFailedException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
@@ -14,27 +17,47 @@ namespace Umbraco.Core.Exceptions
|
||||
public const string DefaultMessage = "Boot failed: Umbraco cannot run. See Umbraco's log file for more details.";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Exception"/> class with a specified error message.
|
||||
/// Initializes a new instance of the <see cref="BootFailedException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error. </param>
|
||||
public BootFailedException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Exception" /> class with a specified error message.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public BootFailedException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Exception"/> class with a specified error message
|
||||
/// Initializes a new instance of the <see cref="Exception" /> class with a specified error message
|
||||
/// and a reference to the inner exception which is the cause of this exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error. </param>
|
||||
/// <param name="inner">The inner exception, or null.</param>
|
||||
public BootFailedException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
/// <param name="innerException">The inner exception, or null.</param>
|
||||
public BootFailedException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Rethrows a captured <see cref="BootFailedException"/>.
|
||||
/// Initializes a new instance of the <see cref="BootFailedException" /> class.
|
||||
/// </summary>
|
||||
/// <remarks>The exception can be null, in which case a default message is used.</remarks>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected BootFailedException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Rethrows a captured <see cref="BootFailedException" />.
|
||||
/// </summary>
|
||||
/// <param name="bootFailedException">The boot failed exception.</param>
|
||||
/// <exception cref="Umbraco.Core.Exceptions.BootFailedException">
|
||||
/// </exception>
|
||||
/// <remarks>
|
||||
/// The exception can be null, in which case a default message is used.
|
||||
/// </remarks>
|
||||
public static void Rethrow(BootFailedException bootFailedException)
|
||||
{
|
||||
if (bootFailedException == null)
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
internal class ConnectionException : Exception
|
||||
{
|
||||
public ConnectionException(string message, Exception innerException) : base(message, innerException)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,98 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
internal class DataOperationException<T> : Exception
|
||||
where T : Enum
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the operation.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The operation.
|
||||
/// </value>
|
||||
/// <remarks>
|
||||
/// This object should be serializable to prevent a <see cref="SerializationException" /> to be thrown.
|
||||
/// </remarks>
|
||||
public T Operation { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
|
||||
/// </summary>
|
||||
public DataOperationException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public DataOperationException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public DataOperationException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="operation">The operation.</param>
|
||||
public DataOperationException(T operation)
|
||||
: this(operation, "Data operation exception: " + operation)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="operation">The operation.</param>
|
||||
/// <param name="message">The message.</param>
|
||||
public DataOperationException(T operation, string message)
|
||||
: base(message)
|
||||
{
|
||||
Operation = operation;
|
||||
}
|
||||
|
||||
public DataOperationException(T operation)
|
||||
: base("Data operation exception: " + operation)
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="DataOperationException{T}" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
/// <exception cref="ArgumentNullException">info</exception>
|
||||
protected DataOperationException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
Operation = operation;
|
||||
Operation = (T)Enum.Parse(typeof(T), info.GetString(nameof(Operation)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
/// <exception cref="ArgumentNullException">info</exception>
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
|
||||
info.AddValue(nameof(Operation), Enum.GetName(typeof(T), Operation));
|
||||
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,44 +1,126 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when a composition is invalid.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class InvalidCompositionException : Exception
|
||||
{
|
||||
public InvalidCompositionException(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliass)
|
||||
{
|
||||
ContentTypeAlias = contentTypeAlias;
|
||||
AddedCompositionAlias = addedCompositionAlias;
|
||||
PropertyTypeAliases = propertyTypeAliass;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the content type alias.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The content type alias.
|
||||
/// </value>
|
||||
public string ContentTypeAlias { get; }
|
||||
|
||||
public InvalidCompositionException(string contentTypeAlias, string[] propertyTypeAliass)
|
||||
{
|
||||
ContentTypeAlias = contentTypeAlias;
|
||||
PropertyTypeAliases = propertyTypeAliass;
|
||||
}
|
||||
/// <summary>
|
||||
/// Gets the added composition alias.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The added composition alias.
|
||||
/// </value>
|
||||
public string AddedCompositionAlias { get; }
|
||||
|
||||
public string ContentTypeAlias { get; private set; }
|
||||
/// <summary>
|
||||
/// Gets the property type aliases.
|
||||
/// </summary>
|
||||
/// <value>
|
||||
/// The property type aliases.
|
||||
/// </value>
|
||||
public string[] PropertyTypeAliases { get; }
|
||||
|
||||
public string AddedCompositionAlias { get; private set; }
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
|
||||
/// </summary>
|
||||
public InvalidCompositionException()
|
||||
{ }
|
||||
|
||||
public string[] PropertyTypeAliases { get; private set; }
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="contentTypeAlias">The content type alias.</param>
|
||||
/// <param name="propertyTypeAliases">The property type aliases.</param>
|
||||
public InvalidCompositionException(string contentTypeAlias, string[] propertyTypeAliases)
|
||||
: this(contentTypeAlias, null, propertyTypeAliases)
|
||||
{ }
|
||||
|
||||
public override string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return AddedCompositionAlias.IsNullOrWhiteSpace()
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="contentTypeAlias">The content type alias.</param>
|
||||
/// <param name="addedCompositionAlias">The added composition alias.</param>
|
||||
/// <param name="propertyTypeAliases">The property type aliases.</param>
|
||||
public InvalidCompositionException(string contentTypeAlias, string addedCompositionAlias, string[] propertyTypeAliases)
|
||||
: this(addedCompositionAlias.IsNullOrWhiteSpace()
|
||||
? string.Format(
|
||||
"ContentType with alias '{0}' has an invalid composition " +
|
||||
"and there was a conflict on the following PropertyTypes: '{1}'. " +
|
||||
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
|
||||
ContentTypeAlias, string.Join(", ", PropertyTypeAliases))
|
||||
contentTypeAlias, string.Join(", ", propertyTypeAliases))
|
||||
: string.Format(
|
||||
"ContentType with alias '{0}' was added as a Composition to ContentType with alias '{1}', " +
|
||||
"but there was a conflict on the following PropertyTypes: '{2}'. " +
|
||||
"PropertyTypes must have a unique alias across all Compositions in order to compose a valid ContentType Composition.",
|
||||
AddedCompositionAlias, ContentTypeAlias, string.Join(", ", PropertyTypeAliases));
|
||||
addedCompositionAlias, contentTypeAlias, string.Join(", ", propertyTypeAliases)))
|
||||
{
|
||||
ContentTypeAlias = contentTypeAlias;
|
||||
AddedCompositionAlias = addedCompositionAlias;
|
||||
PropertyTypeAliases = propertyTypeAliases;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public InvalidCompositionException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public InvalidCompositionException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="InvalidCompositionException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected InvalidCompositionException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{
|
||||
ContentTypeAlias = info.GetString(nameof(ContentTypeAlias));
|
||||
AddedCompositionAlias = info.GetString(nameof(AddedCompositionAlias));
|
||||
PropertyTypeAliases = (string[])info.GetValue(nameof(PropertyTypeAliases), typeof(string[]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When overridden in a derived class, sets the <see cref="T:System.Runtime.Serialization.SerializationInfo" /> with information about the exception.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
/// <exception cref="ArgumentNullException">info</exception>
|
||||
public override void GetObjectData(SerializationInfo info, StreamingContext context)
|
||||
{
|
||||
if (info == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(info));
|
||||
}
|
||||
|
||||
info.AddValue(nameof(ContentTypeAlias), ContentTypeAlias);
|
||||
info.AddValue(nameof(AddedCompositionAlias), AddedCompositionAlias);
|
||||
info.AddValue(nameof(PropertyTypeAliases), PropertyTypeAliases);
|
||||
|
||||
base.GetObjectData(info, context);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an internal exception that in theory should never been thrown, it is only thrown in circumstances that should never happen.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Serializable]
|
||||
public class PanicException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PanicException" /> class.
|
||||
/// </summary>
|
||||
public PanicException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PanicException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public PanicException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PanicException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public PanicException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PanicException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected PanicException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
@@ -1,27 +1,52 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.Exceptions
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when a requested method or operation is not, and will not be, implemented.
|
||||
/// </summary>
|
||||
/// <remarks>The <see cref="NotImplementedException"/> is to be used when some code is not implemented,
|
||||
/// <remarks>
|
||||
/// The <see cref="NotImplementedException" /> is to be used when some code is not implemented,
|
||||
/// but should eventually be implemented (i.e. work in progress) and is reported by tools such as ReSharper.
|
||||
/// This exception is to be used when some code is not implemented, and is not meant to be, for whatever
|
||||
/// reason.</remarks>
|
||||
/// reason.
|
||||
/// </remarks>
|
||||
/// <seealso cref="System.NotImplementedException" />
|
||||
[Serializable]
|
||||
[Obsolete("If a method or operation is not, and will not be, implemented, it is invalid or not supported, so we should throw either an InvalidOperationException or NotSupportedException instead.")]
|
||||
public class WontImplementException : NotImplementedException
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WontImplementException"/> class.
|
||||
/// Initializes a new instance of the <see cref="WontImplementException" /> class.
|
||||
/// </summary>
|
||||
public WontImplementException()
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WontImplementException"/> class with a specified reason message.
|
||||
/// Initializes a new instance of the <see cref="WontImplementException" /> class with a specified reason message.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
public WontImplementException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WontImplementException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="inner">The exception that is the cause of the current exception. If the <paramref name="inner" /> parameter is not <see langword="null" />, the current exception is raised in a <see langword="catch" /> block that handles the inner exception.</param>
|
||||
public WontImplementException(string message, Exception inner)
|
||||
: base(message, inner)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="WontImplementException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected WontImplementException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ namespace Umbraco.Core
|
||||
|
||||
internal void AddDateTime(DateTime d)
|
||||
{
|
||||
_writer.Write(d.Ticks);;
|
||||
_writer.Write(d.Ticks);
|
||||
}
|
||||
|
||||
internal void AddString(string s)
|
||||
|
||||
@@ -1,20 +1,46 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
{
|
||||
/// <summary>
|
||||
/// The exception that is thrown when the caller does not have the required permission to access a file.
|
||||
/// </summary>
|
||||
/// <seealso cref="System.Exception" />
|
||||
[Obsolete("Throw an UnauthorizedAccessException instead.")]
|
||||
[Serializable]
|
||||
public class FileSecurityException : Exception
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
|
||||
/// </summary>
|
||||
public FileSecurityException()
|
||||
{
|
||||
{ }
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The message that describes the error.</param>
|
||||
public FileSecurityException(string message)
|
||||
: base(message)
|
||||
{ }
|
||||
|
||||
public FileSecurityException(string message) : base(message)
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (<see langword="Nothing" /> in Visual Basic) if no inner exception is specified.</param>
|
||||
public FileSecurityException(string message, Exception innerException)
|
||||
: base(message, innerException)
|
||||
{ }
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FileSecurityException" /> class.
|
||||
/// </summary>
|
||||
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo" /> that holds the serialized object data about the exception being thrown.</param>
|
||||
/// <param name="context">The <see cref="T:System.Runtime.Serialization.StreamingContext" /> that contains contextual information about the source or destination.</param>
|
||||
protected FileSecurityException(SerializationInfo info, StreamingContext context)
|
||||
: base(info, context)
|
||||
{ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,10 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Configuration;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Configuration.UmbracoSettings;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.Media;
|
||||
using Umbraco.Core.Models;
|
||||
|
||||
namespace Umbraco.Core.IO
|
||||
@@ -92,7 +87,8 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
if (propertyType == null) throw new ArgumentNullException(nameof(propertyType));
|
||||
if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentNullOrEmptyException(nameof(filename));
|
||||
if (filename == null) throw new ArgumentNullException(nameof(filename));
|
||||
if (string.IsNullOrWhiteSpace(filename)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(filename));
|
||||
if (filestream == null) throw new ArgumentNullException(nameof(filestream));
|
||||
|
||||
// clear the old file, if any
|
||||
@@ -111,7 +107,8 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
if (content == null) throw new ArgumentNullException(nameof(content));
|
||||
if (propertyType == null) throw new ArgumentNullException(nameof(propertyType));
|
||||
if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentNullOrEmptyException(nameof(sourcepath));
|
||||
if (sourcepath == null) throw new ArgumentNullException(nameof(sourcepath));
|
||||
if (string.IsNullOrWhiteSpace(sourcepath)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(sourcepath));
|
||||
|
||||
// ensure we have a file to copy
|
||||
if (FileExists(sourcepath) == false) return null;
|
||||
|
||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Composing;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using System.Threading;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
@@ -39,9 +38,11 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public PhysicalFileSystem(string rootPath, string rootUrl)
|
||||
{
|
||||
if (string.IsNullOrEmpty(rootPath)) throw new ArgumentNullOrEmptyException(nameof(rootPath));
|
||||
if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentNullOrEmptyException(nameof(rootUrl));
|
||||
if (rootPath.StartsWith("~/")) throw new ArgumentException("The rootPath argument cannot be a virtual path and cannot start with '~/'");
|
||||
if (rootPath == null) throw new ArgumentNullException(nameof(rootPath));
|
||||
if (string.IsNullOrEmpty(rootPath)) throw new ArgumentException("Value can't be empty.", nameof(rootPath));
|
||||
if (rootUrl == null) throw new ArgumentNullException(nameof(rootUrl));
|
||||
if (string.IsNullOrEmpty(rootUrl)) throw new ArgumentException("Value can't be empty.", nameof(rootUrl));
|
||||
if (rootPath.StartsWith("~/")) throw new ArgumentException("Value can't be a virtual path and start with '~/'.", nameof(rootPath));
|
||||
|
||||
// rootPath should be... rooted, as in, it's a root path!
|
||||
if (Path.IsPathRooted(rootPath) == false)
|
||||
@@ -314,7 +315,7 @@ namespace Umbraco.Core.IO
|
||||
|
||||
// nothing prevents us to reach the file, security-wise, yet it is outside
|
||||
// this filesystem's root - throw
|
||||
throw new FileSecurityException("File '" + opath + "' is outside this filesystem's root.");
|
||||
throw new UnauthorizedAccessException("File '" + opath + "' is outside this filesystem's root.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public static string TempFileUploads => TempData + "/FileUploads";
|
||||
|
||||
public static string TempImageUploads => TempFileUploads + "/rte";
|
||||
|
||||
public static string Install => "~/install";
|
||||
|
||||
public static string AppCode => "~/App_Code";
|
||||
@@ -27,13 +29,13 @@ namespace Umbraco.Core.IO
|
||||
|
||||
public static string MacroPartials => MvcViews + "/MacroPartials/";
|
||||
|
||||
public static string Media => IOHelper.ReturnPath("umbracoMediaPath", "~/media");
|
||||
public static string Media => IOHelper.ReturnPath(Constants.AppSettings.MediaPath, "~/media");
|
||||
|
||||
public static string Scripts => IOHelper.ReturnPath("umbracoScriptsPath", "~/scripts");
|
||||
public static string Scripts => IOHelper.ReturnPath(Constants.AppSettings.ScriptsPath, "~/scripts");
|
||||
|
||||
public static string Css => IOHelper.ReturnPath("umbracoCssPath", "~/css");
|
||||
public static string Css => IOHelper.ReturnPath(Constants.AppSettings.CssPath, "~/css");
|
||||
|
||||
public static string Umbraco => IOHelper.ReturnPath("umbracoPath", "~/umbraco");
|
||||
public static string Umbraco => IOHelper.ReturnPath(Constants.AppSettings.Path, "~/umbraco");
|
||||
|
||||
public static string Packages => Data + "/packages";
|
||||
|
||||
|
||||
@@ -26,26 +26,32 @@ namespace Umbraco.Core.Logging.Viewer
|
||||
/// A count of number of errors
|
||||
/// By counting Warnings with Exceptions, Errors & Fatal messages
|
||||
/// </summary>
|
||||
int GetNumberOfErrors(DateTimeOffset startDate, DateTimeOffset endDate);
|
||||
int GetNumberOfErrors(LogTimePeriod logTimePeriod);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a number of the different log level entries
|
||||
/// </summary>
|
||||
LogLevelCounts GetLogLevelCounts(DateTimeOffset startDate, DateTimeOffset endDate);
|
||||
LogLevelCounts GetLogLevelCounts(LogTimePeriod logTimePeriod);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of all unique message templates and their counts
|
||||
/// </summary>
|
||||
IEnumerable<LogTemplate> GetMessageTemplates(DateTimeOffset startDate, DateTimeOffset endDate);
|
||||
IEnumerable<LogTemplate> GetMessageTemplates(LogTimePeriod logTimePeriod);
|
||||
|
||||
bool CanHandleLargeLogs { get; }
|
||||
|
||||
bool CheckCanOpenLogs(DateTimeOffset startDate, DateTimeOffset endDate);
|
||||
bool CheckCanOpenLogs(LogTimePeriod logTimePeriod);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current Serilog minimum log level
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
string GetLogLevel();
|
||||
|
||||
/// <summary>
|
||||
/// Returns the collection of logs
|
||||
/// </summary>
|
||||
PagedResult<LogMessage> GetLogs(DateTimeOffset startDate, DateTimeOffset endDate,
|
||||
PagedResult<LogMessage> GetLogs(LogTimePeriod logTimePeriod,
|
||||
int pageNumber = 1,
|
||||
int pageSize = 100,
|
||||
Direction orderDirection = Direction.Descending,
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Umbraco.Core.Logging.Viewer
|
||||
|
||||
public override bool CanHandleLargeLogs => false;
|
||||
|
||||
public override bool CheckCanOpenLogs(DateTimeOffset startDate, DateTimeOffset endDate)
|
||||
public override bool CheckCanOpenLogs(LogTimePeriod logTimePeriod)
|
||||
{
|
||||
//Log Directory
|
||||
var logDirectory = _logsPath;
|
||||
@@ -36,7 +36,7 @@ namespace Umbraco.Core.Logging.Viewer
|
||||
|
||||
//foreach full day in the range - see if we can find one or more filenames that end with
|
||||
//yyyyMMdd.json - Ends with due to MachineName in filenames - could be 1 or more due to load balancing
|
||||
for (var day = startDate.Date; day.Date <= endDate.Date; day = day.AddDays(1))
|
||||
for (var day = logTimePeriod.StartTime.Date; day.Date <= logTimePeriod.EndTime.Date; day = day.AddDays(1))
|
||||
{
|
||||
//Filename ending to search for (As could be multiple)
|
||||
var filesToFind = GetSearchPattern(day);
|
||||
@@ -57,7 +57,7 @@ namespace Umbraco.Core.Logging.Viewer
|
||||
return $"*{day:yyyyMMdd}*.json";
|
||||
}
|
||||
|
||||
protected override IReadOnlyList<LogEvent> GetLogs(DateTimeOffset startDate, DateTimeOffset endDate, ILogFilter filter, int skip, int take)
|
||||
protected override IReadOnlyList<LogEvent> GetLogs(LogTimePeriod logTimePeriod, ILogFilter filter, int skip, int take)
|
||||
{
|
||||
var logs = new List<LogEvent>();
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Umbraco.Core.Logging.Viewer
|
||||
|
||||
//foreach full day in the range - see if we can find one or more filenames that end with
|
||||
//yyyyMMdd.json - Ends with due to MachineName in filenames - could be 1 or more due to load balancing
|
||||
for (var day = startDate.Date; day.Date <= endDate.Date; day = day.AddDays(1))
|
||||
for (var day = logTimePeriod.StartTime.Date; day.Date <= logTimePeriod.EndTime.Date; day = day.AddDays(1))
|
||||
{
|
||||
//Filename ending to search for (As could be multiple)
|
||||
var filesToFind = GetSearchPattern(day);
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Logging.Viewer
|
||||
{
|
||||
public class LogTimePeriod
|
||||
{
|
||||
public LogTimePeriod(DateTime startTime, DateTime endTime)
|
||||
{
|
||||
StartTime = startTime;
|
||||
EndTime = endTime;
|
||||
}
|
||||
|
||||
public DateTime StartTime { get; }
|
||||
public DateTime EndTime { get; }
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml;
|
||||
using Newtonsoft.Json;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Models;
|
||||
using Umbraco.Core.Persistence.DatabaseModelDefinitions;
|
||||
using Formatting = Newtonsoft.Json.Formatting;
|
||||
|
||||
namespace Umbraco.Core.Logging.Viewer
|
||||
{
|
||||
@@ -27,9 +30,9 @@ namespace Umbraco.Core.Logging.Viewer
|
||||
/// <summary>
|
||||
/// Get all logs from your chosen data source back as Serilog LogEvents
|
||||
/// </summary>
|
||||
protected abstract IReadOnlyList<LogEvent> GetLogs(DateTimeOffset startDate, DateTimeOffset endDate, ILogFilter filter, int skip, int take);
|
||||
protected abstract IReadOnlyList<LogEvent> GetLogs(LogTimePeriod logTimePeriod, ILogFilter filter, int skip, int take);
|
||||
|
||||
public abstract bool CheckCanOpenLogs(DateTimeOffset startDate, DateTimeOffset endDate);
|
||||
public abstract bool CheckCanOpenLogs(LogTimePeriod logTimePeriod);
|
||||
|
||||
public virtual IReadOnlyList<SavedLogSearch> GetSavedSearches()
|
||||
{
|
||||
@@ -82,24 +85,34 @@ namespace Umbraco.Core.Logging.Viewer
|
||||
return searches;
|
||||
}
|
||||
|
||||
public int GetNumberOfErrors(DateTimeOffset startDate, DateTimeOffset endDate)
|
||||
public int GetNumberOfErrors(LogTimePeriod logTimePeriod)
|
||||
{
|
||||
var errorCounter = new ErrorCounterFilter();
|
||||
GetLogs(startDate, endDate, errorCounter, 0, int.MaxValue);
|
||||
GetLogs(logTimePeriod, errorCounter, 0, int.MaxValue);
|
||||
return errorCounter.Count;
|
||||
}
|
||||
|
||||
public LogLevelCounts GetLogLevelCounts(DateTimeOffset startDate, DateTimeOffset endDate)
|
||||
/// <summary>
|
||||
/// Get the Serilog minimum-level value from the config file.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public string GetLogLevel()
|
||||
{
|
||||
var logLevel = Enum.GetValues(typeof(LogEventLevel)).Cast<LogEventLevel>().Where(Log.Logger.IsEnabled)?.Min() ?? null;
|
||||
return logLevel?.ToString() ?? "";
|
||||
}
|
||||
|
||||
public LogLevelCounts GetLogLevelCounts(LogTimePeriod logTimePeriod)
|
||||
{
|
||||
var counter = new CountingFilter();
|
||||
GetLogs(startDate, endDate, counter, 0, int.MaxValue);
|
||||
GetLogs(logTimePeriod, counter, 0, int.MaxValue);
|
||||
return counter.Counts;
|
||||
}
|
||||
|
||||
public IEnumerable<LogTemplate> GetMessageTemplates(DateTimeOffset startDate, DateTimeOffset endDate)
|
||||
public IEnumerable<LogTemplate> GetMessageTemplates(LogTimePeriod logTimePeriod)
|
||||
{
|
||||
var messageTemplates = new MessageTemplateFilter();
|
||||
GetLogs(startDate, endDate, messageTemplates, 0, int.MaxValue);
|
||||
GetLogs(logTimePeriod, messageTemplates, 0, int.MaxValue);
|
||||
|
||||
var templates = messageTemplates.Counts.
|
||||
Select(x => new LogTemplate { MessageTemplate = x.Key, Count = x.Value })
|
||||
@@ -108,14 +121,14 @@ namespace Umbraco.Core.Logging.Viewer
|
||||
return templates;
|
||||
}
|
||||
|
||||
public PagedResult<LogMessage> GetLogs(DateTimeOffset startDate, DateTimeOffset endDate,
|
||||
public PagedResult<LogMessage> GetLogs(LogTimePeriod logTimePeriod,
|
||||
int pageNumber = 1, int pageSize = 100,
|
||||
Direction orderDirection = Direction.Descending,
|
||||
string filterExpression = null,
|
||||
string[] logLevels = null)
|
||||
{
|
||||
var expression = new ExpressionFilter(filterExpression);
|
||||
var filteredLogs = GetLogs(startDate, endDate, expression, 0, int.MaxValue);
|
||||
var filteredLogs = GetLogs(logTimePeriod, expression, 0, int.MaxValue);
|
||||
|
||||
//This is user used the checkbox UI to toggle which log levels they wish to see
|
||||
//If an empty array or null - its implied all levels to be viewed
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Web.Hosting;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
namespace Umbraco.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the full implementation of <see cref="IMainDom"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When an AppDomain starts, it tries to acquire the main domain status.</para>
|
||||
/// <para>When an AppDomain stops (eg the application is restarting) it should release the main domain status.</para>
|
||||
/// </remarks>
|
||||
internal class MainDom : IMainDom, IRegisteredObject
|
||||
{
|
||||
#region Vars
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
// our own lock for local consistency
|
||||
private readonly object _locko = new object();
|
||||
|
||||
// async lock representing the main domain lock
|
||||
private readonly AsyncLock _asyncLock;
|
||||
private IDisposable _asyncLocker;
|
||||
|
||||
// event wait handle used to notify current main domain that it should
|
||||
// release the lock because a new domain wants to be the main domain
|
||||
private readonly EventWaitHandle _signal;
|
||||
|
||||
// indicates whether...
|
||||
private volatile bool _isMainDom; // we are the main domain
|
||||
private volatile bool _signaled; // we have been signaled
|
||||
|
||||
// actions to run before releasing the main domain
|
||||
private readonly List<KeyValuePair<int, Action>> _callbacks = new List<KeyValuePair<int, Action>>();
|
||||
|
||||
private const int LockTimeoutMilliseconds = 90000; // (1.5 * 60 * 1000) == 1 min 30 seconds
|
||||
|
||||
#endregion
|
||||
|
||||
#region Ctor
|
||||
|
||||
// initializes a new instance of MainDom
|
||||
public MainDom(ILogger logger)
|
||||
{
|
||||
_logger = logger;
|
||||
|
||||
var appId = string.Empty;
|
||||
// HostingEnvironment.ApplicationID is null in unit tests, making ReplaceNonAlphanumericChars fail
|
||||
if (HostingEnvironment.ApplicationID != null)
|
||||
appId = HostingEnvironment.ApplicationID.ReplaceNonAlphanumericChars(string.Empty);
|
||||
|
||||
// combining with the physical path because if running on eg IIS Express,
|
||||
// two sites could have the same appId even though they are different.
|
||||
//
|
||||
// now what could still collide is... two sites, running in two different processes
|
||||
// and having the same appId, and running on the same app physical path
|
||||
//
|
||||
// we *cannot* use the process ID here because when an AppPool restarts it is
|
||||
// a new process for the same application path
|
||||
|
||||
var appPath = HostingEnvironment.ApplicationPhysicalPath;
|
||||
var hash = (appId + ":::" + appPath).ToSHA1();
|
||||
|
||||
var lockName = "UMBRACO-" + hash + "-MAINDOM-LCK";
|
||||
_asyncLock = new AsyncLock(lockName);
|
||||
|
||||
var eventName = "UMBRACO-" + hash + "-MAINDOM-EVT";
|
||||
_signal = new EventWaitHandle(false, EventResetMode.AutoReset, eventName);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Registers a resource that requires the current AppDomain to be the main domain to function.
|
||||
/// </summary>
|
||||
/// <param name="release">An action to execute before the AppDomain releases the main domain status.</param>
|
||||
/// <param name="weight">An optional weight (lower goes first).</param>
|
||||
/// <returns>A value indicating whether it was possible to register.</returns>
|
||||
public bool Register(Action release, int weight = 100)
|
||||
=> Register(null, release, weight);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a resource that requires the current AppDomain to be the main domain to function.
|
||||
/// </summary>
|
||||
/// <param name="install">An action to execute when registering.</param>
|
||||
/// <param name="release">An action to execute before the AppDomain releases the main domain status.</param>
|
||||
/// <param name="weight">An optional weight (lower goes first).</param>
|
||||
/// <returns>A value indicating whether it was possible to register.</returns>
|
||||
/// <remarks>If registering is successful, then the <paramref name="install"/> action
|
||||
/// is guaranteed to execute before the AppDomain releases the main domain status.</remarks>
|
||||
public bool Register(Action install, Action release, int weight = 100)
|
||||
{
|
||||
lock (_locko)
|
||||
{
|
||||
if (_signaled) return false;
|
||||
install?.Invoke();
|
||||
if (release != null)
|
||||
_callbacks.Add(new KeyValuePair<int, Action>(weight, release));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// handles the signal requesting that the main domain is released
|
||||
private void OnSignal(string source)
|
||||
{
|
||||
// once signaled, we stop waiting, but then there is the hosting environment
|
||||
// so we have to make sure that we only enter that method once
|
||||
|
||||
lock (_locko)
|
||||
{
|
||||
_logger.Debug<MainDom>("Signaled {Signaled} ({SignalSource})", _signaled ? "(again)" : string.Empty, source);
|
||||
if (_signaled) return;
|
||||
if (_isMainDom == false) return; // probably not needed
|
||||
_signaled = true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Info<MainDom>("Stopping ({SignalSource})", source);
|
||||
foreach (var callback in _callbacks.OrderBy(x => x.Key).Select(x => x.Value))
|
||||
{
|
||||
try
|
||||
{
|
||||
callback(); // no timeout on callbacks
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_logger.Error<MainDom>(e, "Error while running callback, remaining callbacks will not run.");
|
||||
throw;
|
||||
}
|
||||
|
||||
}
|
||||
_logger.Debug<MainDom>("Stopped ({SignalSource})", source);
|
||||
}
|
||||
finally
|
||||
{
|
||||
// in any case...
|
||||
_isMainDom = false;
|
||||
_asyncLocker.Dispose();
|
||||
_logger.Info<MainDom>("Released ({SignalSource})", source);
|
||||
}
|
||||
}
|
||||
|
||||
// acquires the main domain
|
||||
internal bool Acquire()
|
||||
{
|
||||
lock (_locko) // we don't want the hosting environment to interfere by signaling
|
||||
{
|
||||
// if signaled, too late to acquire, give up
|
||||
// the handler is not installed so that would be the hosting environment
|
||||
if (_signaled)
|
||||
{
|
||||
_logger.Info<MainDom>("Cannot acquire (signaled).");
|
||||
return false;
|
||||
}
|
||||
|
||||
_logger.Info<MainDom>("Acquiring.");
|
||||
|
||||
// signal other instances that we want the lock, then wait one the lock,
|
||||
// which may timeout, and this is accepted - see comments below
|
||||
|
||||
// signal, then wait for the lock, then make sure the event is
|
||||
// reset (maybe there was noone listening..)
|
||||
_signal.Set();
|
||||
|
||||
// if more than 1 instance reach that point, one will get the lock
|
||||
// and the other one will timeout, which is accepted
|
||||
|
||||
_asyncLocker = _asyncLock.Lock(LockTimeoutMilliseconds);
|
||||
_isMainDom = true;
|
||||
|
||||
// we need to reset the event, because otherwise we would end up
|
||||
// signaling ourselves and committing suicide immediately.
|
||||
// only 1 instance can reach that point, but other instances may
|
||||
// have started and be trying to get the lock - they will timeout,
|
||||
// which is accepted
|
||||
|
||||
_signal.Reset();
|
||||
_signal.WaitOneAsync()
|
||||
.ContinueWith(_ => OnSignal("signal"));
|
||||
|
||||
HostingEnvironment.RegisterObject(this);
|
||||
|
||||
_logger.Info<MainDom>("Acquired.");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether the current domain is the main domain.
|
||||
/// </summary>
|
||||
public bool IsMainDom => _isMainDom;
|
||||
|
||||
// IRegisteredObject
|
||||
void IRegisteredObject.Stop(bool immediate)
|
||||
{
|
||||
try
|
||||
{
|
||||
OnSignal("environment"); // will run once
|
||||
}
|
||||
finally
|
||||
{
|
||||
HostingEnvironment.UnregisterObject(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides filtering for package manifests.
|
||||
/// </summary>
|
||||
public interface IManifestFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// Filters package manifests.
|
||||
/// </summary>
|
||||
/// <param name="manifests">The package manifests.</param>
|
||||
/// <remarks>
|
||||
/// <para>It is possible to remove, change, or add manifests.</para>
|
||||
/// </remarks>
|
||||
void Filter(List<PackageManifest> manifests);
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,8 @@ namespace Umbraco.Core.Manifest
|
||||
// '-content/foo', // hide for content type 'foo'
|
||||
// '+content/*', // show for all other content types
|
||||
// '+media/*', // show for all media types
|
||||
// '-member/foo' // hide for member type 'foo'
|
||||
// '+member/*' // show for all member types
|
||||
// '+role/admin' // show for admin users. Role based permissions will override others.
|
||||
// ]
|
||||
// },
|
||||
@@ -56,6 +58,10 @@ namespace Umbraco.Core.Manifest
|
||||
partA = "media";
|
||||
partB = media.ContentType.Alias;
|
||||
break;
|
||||
case IMember member:
|
||||
partA = "member";
|
||||
partB = member.ContentType.Alias;
|
||||
break;
|
||||
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains the manifest filters.
|
||||
/// </summary>
|
||||
public class ManifestFilterCollection : BuilderCollectionBase<IManifestFilter>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManifestFilterCollection"/> class.
|
||||
/// </summary>
|
||||
public ManifestFilterCollection(IEnumerable<IManifestFilter> items)
|
||||
: base(items)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Filters package manifests.
|
||||
/// </summary>
|
||||
/// <param name="manifests">The package manifests.</param>
|
||||
public void Filter(List<PackageManifest> manifests)
|
||||
{
|
||||
foreach (var filter in this)
|
||||
filter.Filter(manifests);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using Umbraco.Core.Composing;
|
||||
|
||||
namespace Umbraco.Core.Manifest
|
||||
{
|
||||
public class ManifestFilterCollectionBuilder : OrderedCollectionBuilderBase<ManifestFilterCollectionBuilder, ManifestFilterCollection, IManifestFilter>
|
||||
{
|
||||
protected override ManifestFilterCollectionBuilder This => this;
|
||||
|
||||
// do NOT cache this, it's only used once
|
||||
protected override Lifetime CollectionLifetime => Lifetime.Transient;
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,6 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
using Umbraco.Core.Cache;
|
||||
using Umbraco.Core.Exceptions;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.PropertyEditors;
|
||||
@@ -22,25 +21,28 @@ namespace Umbraco.Core.Manifest
|
||||
private readonly IAppPolicyCache _cache;
|
||||
private readonly ILogger _logger;
|
||||
private readonly ManifestValueValidatorCollection _validators;
|
||||
private readonly ManifestFilterCollection _filters;
|
||||
|
||||
private string _path;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManifestParser"/> class.
|
||||
/// </summary>
|
||||
public ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ILogger logger)
|
||||
: this(appCaches, validators, "~/App_Plugins", logger)
|
||||
public ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, ILogger logger)
|
||||
: this(appCaches, validators, filters, "~/App_Plugins", logger)
|
||||
{ }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ManifestParser"/> class.
|
||||
/// </summary>
|
||||
private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, string path, ILogger logger)
|
||||
private ManifestParser(AppCaches appCaches, ManifestValueValidatorCollection validators, ManifestFilterCollection filters, string path, ILogger logger)
|
||||
{
|
||||
if (appCaches == null) throw new ArgumentNullException(nameof(appCaches));
|
||||
_cache = appCaches.RuntimeCache;
|
||||
_validators = validators ?? throw new ArgumentNullException(nameof(validators));
|
||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentNullOrEmptyException(nameof(path));
|
||||
_filters = filters ?? throw new ArgumentNullException(nameof(filters));
|
||||
if (path == null) throw new ArgumentNullException(nameof(path));
|
||||
if (string.IsNullOrWhiteSpace(path)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(path));
|
||||
Path = path;
|
||||
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
|
||||
}
|
||||
@@ -78,6 +80,7 @@ namespace Umbraco.Core.Manifest
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
continue;
|
||||
var manifest = ParseManifest(text);
|
||||
manifest.Source = path;
|
||||
manifests.Add(manifest);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -86,6 +89,8 @@ namespace Umbraco.Core.Manifest
|
||||
}
|
||||
}
|
||||
|
||||
_filters.Filter(manifests);
|
||||
|
||||
return manifests;
|
||||
}
|
||||
|
||||
@@ -150,8 +155,8 @@ namespace Umbraco.Core.Manifest
|
||||
/// </summary>
|
||||
internal PackageManifest ParseManifest(string text)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
throw new ArgumentNullOrEmptyException(nameof(text));
|
||||
if (text == null) throw new ArgumentNullException(nameof(text));
|
||||
if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(text));
|
||||
|
||||
var manifest = JsonConvert.DeserializeObject<PackageManifest>(text,
|
||||
new DataEditorConverter(_logger),
|
||||
|
||||
@@ -9,6 +9,16 @@ namespace Umbraco.Core.Manifest
|
||||
/// </summary>
|
||||
public class PackageManifest
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the source path of the manifest.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>Gets the full absolute file path of the manifest,
|
||||
/// using system directory separators.</para>
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public string Source { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the scripts listed in the manifest.
|
||||
/// </summary>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Collections;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Exceptions;
|
||||
|
||||
namespace Umbraco.Core.Mapping
|
||||
{
|
||||
@@ -191,7 +192,7 @@ namespace Umbraco.Core.Mapping
|
||||
private TTarget Map<TTarget>(object source, Type sourceType, MapperContext context)
|
||||
{
|
||||
if (source == null)
|
||||
throw new ArgumentNullException(nameof(source));
|
||||
return default;
|
||||
|
||||
var targetType = typeof(TTarget);
|
||||
|
||||
@@ -231,10 +232,10 @@ namespace Umbraco.Core.Mapping
|
||||
if (ctor != null && map != null)
|
||||
{
|
||||
// register (for next time) and do it now (for this time)
|
||||
object NCtor(object s, MapperContext c) => MapEnumerableInternal<TTarget>((IEnumerable) s, targetGenericArg, ctor, map, c);
|
||||
object NCtor(object s, MapperContext c) => MapEnumerableInternal<TTarget>((IEnumerable)s, targetGenericArg, ctor, map, c);
|
||||
DefineCtors(sourceType)[targetType] = NCtor;
|
||||
DefineMaps(sourceType)[targetType] = Identity;
|
||||
return (TTarget) NCtor(source, context);
|
||||
return (TTarget)NCtor(source, context);
|
||||
}
|
||||
|
||||
throw new InvalidOperationException($"Don't know how to map {sourceGenericArg.FullName} to {targetGenericArg.FullName}, so don't know how to map {sourceType.FullName} to {targetType.FullName}.");
|
||||
@@ -259,13 +260,13 @@ namespace Umbraco.Core.Mapping
|
||||
if (typeof(TTarget).IsArray)
|
||||
{
|
||||
var elementType = typeof(TTarget).GetElementType();
|
||||
if (elementType == null) throw new Exception("panic");
|
||||
if (elementType == null) throw new PanicException("elementType == null which should never occur");
|
||||
var targetArray = Array.CreateInstance(elementType, targetList.Count);
|
||||
targetList.CopyTo(targetArray, 0);
|
||||
target = targetArray;
|
||||
}
|
||||
|
||||
return (TTarget) target;
|
||||
return (TTarget)target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -342,7 +343,21 @@ namespace Umbraco.Core.Mapping
|
||||
|
||||
if (ctor == null) return null;
|
||||
|
||||
_ctors[sourceType] = sourceCtor;
|
||||
_ctors.AddOrUpdate(sourceType, sourceCtor, (k, v) =>
|
||||
{
|
||||
// Add missing constructors
|
||||
foreach (var c in sourceCtor)
|
||||
{
|
||||
if (!v.ContainsKey(c.Key))
|
||||
{
|
||||
v.Add(c.Key, c.Value);
|
||||
}
|
||||
}
|
||||
|
||||
return v;
|
||||
});
|
||||
|
||||
|
||||
return ctor;
|
||||
}
|
||||
|
||||
@@ -367,7 +382,17 @@ namespace Umbraco.Core.Mapping
|
||||
|
||||
if (map == null) return null;
|
||||
|
||||
_maps[sourceType] = sourceMap;
|
||||
if (_maps.ContainsKey(sourceType))
|
||||
{
|
||||
foreach (var m in sourceMap)
|
||||
{
|
||||
if (!_maps[sourceType].TryGetValue(m.Key, out _))
|
||||
_maps[sourceType].Add(m.Key, m.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
_maps[sourceType] = sourceMap;
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -382,7 +407,7 @@ namespace Umbraco.Core.Mapping
|
||||
{
|
||||
if (type.IsArray) return type.GetElementType();
|
||||
if (type.IsGenericType) return type.GenericTypeArguments[0];
|
||||
throw new Exception("panic");
|
||||
throw new PanicException($"Could not get enumerable or array type from {type}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Umbraco.Core.Migrations
|
||||
{
|
||||
/// <summary>
|
||||
/// Used if a migration has executed but the whole process has failed and cannot be rolled back
|
||||
/// </summary>
|
||||
internal class DataLossException : Exception
|
||||
{
|
||||
public DataLossException(string msg)
|
||||
: base(msg)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public DataLossException(string msg, Exception inner)
|
||||
: base(msg, inner)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -25,11 +25,25 @@ namespace Umbraco.Core.Migrations.Expressions.Create.KeysAndIndexes
|
||||
var syntax = _context.SqlContext.SqlSyntax;
|
||||
var tableDefinition = DefinitionFactory.GetTableDefinition(TypeOfDto, syntax);
|
||||
|
||||
// note: of course we are creating the keys and indexes as per the DTO, so
|
||||
// changing the DTO may break old migrations - or, better, these migrations
|
||||
// should capture a copy of the DTO class that will not change
|
||||
|
||||
ExecuteSql(syntax.FormatPrimaryKey(tableDefinition));
|
||||
foreach (var sql in syntax.Format(tableDefinition.Indexes))
|
||||
ExecuteSql(sql);
|
||||
foreach (var sql in syntax.Format(tableDefinition.ForeignKeys))
|
||||
ExecuteSql(sql);
|
||||
|
||||
// note: we do *not* create the DF_ default constraints
|
||||
/*
|
||||
foreach (var column in tableDefinition.Columns)
|
||||
{
|
||||
var sql = syntax.FormatDefaultConstraint(column);
|
||||
if (!sql.IsNullOrWhiteSpace())
|
||||
ExecuteSql(sql);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
private void ExecuteSql(string sql)
|
||||
|
||||
+9
-2
@@ -10,9 +10,13 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.DefaultConstraint
|
||||
IDeleteDefaultConstraintOnTableBuilder,
|
||||
IDeleteDefaultConstraintOnColumnBuilder
|
||||
{
|
||||
public DeleteDefaultConstraintBuilder(DeleteDefaultConstraintExpression expression)
|
||||
private readonly IMigrationContext _context;
|
||||
|
||||
public DeleteDefaultConstraintBuilder(IMigrationContext context, DeleteDefaultConstraintExpression expression)
|
||||
: base(expression)
|
||||
{ }
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IDeleteDefaultConstraintOnColumnBuilder OnTable(string tableName)
|
||||
@@ -25,6 +29,9 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.DefaultConstraint
|
||||
public IExecutableBuilder OnColumn(string columnName)
|
||||
{
|
||||
Expression.ColumnName = columnName;
|
||||
Expression.HasDefaultConstraint = _context.SqlContext.SqlSyntax.TryGetDefaultConstraint(_context.Database, Expression.TableName, columnName, out var constraintName);
|
||||
Expression.ConstraintName = constraintName ?? string.Empty;
|
||||
|
||||
return new ExecutableBuilder(Expression);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using NPoco;
|
||||
using System;
|
||||
using Umbraco.Core.Migrations.Expressions.Common;
|
||||
using Umbraco.Core.Migrations.Expressions.Delete.Column;
|
||||
using Umbraco.Core.Migrations.Expressions.Delete.Constraint;
|
||||
@@ -29,9 +29,20 @@ namespace Umbraco.Core.Migrations.Expressions.Delete
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IExecutableBuilder KeysAndIndexes(string tableName = null)
|
||||
public IExecutableBuilder KeysAndIndexes<TDto>(bool local = true, bool foreign = true)
|
||||
{
|
||||
return new DeleteKeysAndIndexesBuilder(_context) { TableName = tableName };
|
||||
var syntax = _context.SqlContext.SqlSyntax;
|
||||
var tableDefinition = DefinitionFactory.GetTableDefinition(typeof(TDto), syntax);
|
||||
return KeysAndIndexes(tableDefinition.Name, local, foreign);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public IExecutableBuilder KeysAndIndexes(string tableName, bool local = true, bool foreign = true)
|
||||
{
|
||||
if (tableName == null) throw new ArgumentNullException(nameof(tableName));
|
||||
if (string.IsNullOrWhiteSpace(tableName)) throw new ArgumentException("Value can't be empty or consist only of white-space characters.", nameof(tableName));
|
||||
|
||||
return new DeleteKeysAndIndexesBuilder(_context) { TableName = tableName, DeleteLocal = local, DeleteForeign = foreign };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -100,7 +111,7 @@ namespace Umbraco.Core.Migrations.Expressions.Delete
|
||||
public IDeleteDefaultConstraintOnTableBuilder DefaultConstraint()
|
||||
{
|
||||
var expression = new DeleteDefaultConstraintExpression(_context);
|
||||
return new DeleteDefaultConstraintBuilder(expression);
|
||||
return new DeleteDefaultConstraintBuilder(_context, expression);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-3
@@ -10,12 +10,17 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.Expressions
|
||||
|
||||
public virtual string TableName { get; set; }
|
||||
public virtual string ColumnName { get; set; }
|
||||
public virtual string ConstraintName { get; set; }
|
||||
public virtual bool HasDefaultConstraint { get; set; }
|
||||
|
||||
protected override string GetSql()
|
||||
{
|
||||
return string.Format(SqlSyntax.DeleteDefaultConstraint,
|
||||
SqlSyntax.GetQuotedTableName(TableName),
|
||||
SqlSyntax.GetQuotedColumnName(ColumnName));
|
||||
return HasDefaultConstraint
|
||||
? string.Format(SqlSyntax.DeleteDefaultConstraint,
|
||||
SqlSyntax.GetQuotedTableName(TableName),
|
||||
SqlSyntax.GetQuotedColumnName(ColumnName),
|
||||
SqlSyntax.GetQuotedName(ConstraintName))
|
||||
: string.Empty;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,9 +19,14 @@ namespace Umbraco.Core.Migrations.Expressions.Delete
|
||||
IExecutableBuilder Table(string tableName);
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the table to delete keys and indexes for.
|
||||
/// Builds a Delete Keys and Indexes expression, and executes.
|
||||
/// </summary>
|
||||
IExecutableBuilder KeysAndIndexes(string tableName = null);
|
||||
IExecutableBuilder KeysAndIndexes<TDto>(bool local = true, bool foreign = true);
|
||||
|
||||
/// <summary>
|
||||
/// Builds a Delete Keys and Indexes expression, and executes.
|
||||
/// </summary>
|
||||
IExecutableBuilder KeysAndIndexes(string tableName, bool local = true, bool foreign = true);
|
||||
|
||||
/// <summary>
|
||||
/// Specifies the column to delete.
|
||||
|
||||
+56
-24
@@ -1,5 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NPoco;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Migrations.Expressions.Common;
|
||||
using Umbraco.Core.Persistence.SqlSyntax;
|
||||
|
||||
@@ -18,36 +20,66 @@ namespace Umbraco.Core.Migrations.Expressions.Delete.KeysAndIndexes
|
||||
|
||||
public string TableName { get; set; }
|
||||
|
||||
public bool DeleteLocal { get; set; }
|
||||
|
||||
public bool DeleteForeign { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Do()
|
||||
{
|
||||
if (TableName == null)
|
||||
{
|
||||
// drop keys
|
||||
var keys = _context.SqlContext.SqlSyntax.GetConstraintsPerTable(_context.Database).DistinctBy(x => x.Item2).ToArray();
|
||||
foreach (var key in keys.Where(x => x.Item2.StartsWith("FK_")))
|
||||
Delete.ForeignKey(key.Item2).OnTable(key.Item1).Do();
|
||||
foreach (var key in keys.Where(x => x.Item2.StartsWith("PK_")))
|
||||
Delete.PrimaryKey(key.Item2).FromTable(key.Item1).Do();
|
||||
_context.BuildingExpression = false;
|
||||
|
||||
// drop indexes
|
||||
var indexes = _context.SqlContext.SqlSyntax.GetDefinedIndexesDefinitions(_context.Database).DistinctBy(x => x.IndexName).ToArray();
|
||||
foreach (var index in indexes)
|
||||
Delete.Index(index.IndexName).OnTable(index.TableName).Do();
|
||||
//get a list of all constraints - this will include all PK, FK and unique constraints
|
||||
var tableConstraints = _context.SqlContext.SqlSyntax.GetConstraintsPerTable(_context.Database).DistinctBy(x => x.Item2).ToList();
|
||||
|
||||
//get a list of defined indexes - this will include all indexes, unique indexes and unique constraint indexes
|
||||
var indexes = _context.SqlContext.SqlSyntax.GetDefinedIndexesDefinitions(_context.Database).DistinctBy(x => x.IndexName).ToList();
|
||||
|
||||
var uniqueConstraintNames = tableConstraints.Where(x => !x.Item2.InvariantStartsWith("PK_") && !x.Item2.InvariantStartsWith("FK_")).Select(x => x.Item2);
|
||||
var indexNames = indexes.Select(x => x.IndexName).ToList();
|
||||
|
||||
// drop keys
|
||||
if (DeleteLocal || DeleteForeign)
|
||||
{
|
||||
// table, constraint
|
||||
|
||||
if (DeleteForeign)
|
||||
{
|
||||
//In some cases not all FK's are prefixed with "FK" :/ mostly with old upgraded databases so we need to check if it's either:
|
||||
// * starts with FK OR
|
||||
// * doesn't start with PK_ and doesn't exist in the list of indexes
|
||||
|
||||
foreach (var key in tableConstraints.Where(x => x.Item1 == TableName
|
||||
&& (x.Item2.InvariantStartsWith("FK_") || (!x.Item2.InvariantStartsWith("PK_") && !indexNames.InvariantContains(x.Item2)))))
|
||||
{
|
||||
Delete.ForeignKey(key.Item2).OnTable(key.Item1).Do();
|
||||
}
|
||||
|
||||
}
|
||||
if (DeleteLocal)
|
||||
{
|
||||
foreach (var key in tableConstraints.Where(x => x.Item1 == TableName && x.Item2.InvariantStartsWith("PK_")))
|
||||
Delete.PrimaryKey(key.Item2).FromTable(key.Item1).Do();
|
||||
|
||||
// note: we do *not* delete the DEFAULT constraints and if we wanted to we'd have to deal with that in interesting ways
|
||||
// since SQL server has a specific way to handle that, see SqlServerSyntaxProvider.GetDefaultConstraintsPerColumn
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// drop keys
|
||||
var keys = _context.SqlContext.SqlSyntax.GetConstraintsPerTable(_context.Database).DistinctBy(x => x.Item2).ToArray();
|
||||
foreach (var key in keys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("FK_")))
|
||||
Delete.ForeignKey(key.Item2).OnTable(key.Item1).Do();
|
||||
foreach (var key in keys.Where(x => x.Item1 == TableName && x.Item2.StartsWith("PK_")))
|
||||
Delete.PrimaryKey(key.Item2).FromTable(key.Item1).Do();
|
||||
|
||||
// drop indexes
|
||||
var indexes = _context.SqlContext.SqlSyntax.GetDefinedIndexesDefinitions(_context.Database).DistinctBy(x => x.IndexName).ToArray();
|
||||
// drop indexes
|
||||
if (DeleteLocal)
|
||||
{
|
||||
foreach (var index in indexes.Where(x => x.TableName == TableName))
|
||||
Delete.Index(index.IndexName).OnTable(index.TableName).Do();
|
||||
{
|
||||
//if this is a unique constraint we need to drop the constraint, else drop the index
|
||||
//to figure this out, the index must be tagged as unique and it must exist in the tableConstraints
|
||||
|
||||
if (index.IsUnique && uniqueConstraintNames.InvariantContains(index.IndexName))
|
||||
Delete.UniqueConstraint(index.IndexName).FromTable(index.TableName).Do();
|
||||
else
|
||||
Delete.Index(index.IndexName).OnTable(index.TableName).Do();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ namespace Umbraco.Core.Migrations
|
||||
bool BuildingExpression { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adds a post-migrations.
|
||||
/// Adds a post-migration.
|
||||
/// </summary>
|
||||
void AddPostMigration<TMigration>()
|
||||
where TMigration : IMigration;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user