Merge branch 'v8/dev' into v8/feature/284-auto-mapper

This commit is contained in:
Stephan
2019-04-02 12:03:05 +02:00
175 changed files with 3469 additions and 2852 deletions
+170 -122
View File
@@ -1,14 +1,176 @@
Umbraco Cms Build
--
----
# Umbraco Cms Build
# Quick!
## Are you sure?
To build Umbraco, fire PowerShell and move to Umbraco's repository root (the directory that contains `src`, `build`, `README.md`...). There, trigger the build with the following command:
In order to use Umbraco as a CMS and build your website with it, you should not build it yourself. If you're reading this then you're trying to contribute to Umbraco or you're debugging a complex issue.
- Are you about to create a pull request for Umbraco?
- Are you trying to get to the bottom of a problem in your existing Umbraco installation?
If the answer is yes, please read on. Otherwise, make sure to head on over [to the download page](https://our.umbraco.com/download) and start using Umbraco CMS as intended.
**Table of contents**
[Building from source](#building-from-source)
* [The quick build](#quick)
* [Build infrastructure](#build-infrastructure)
* [Properties](#properties)
* [GetUmbracoVersion](#getumbracoversion)
* [SetUmbracoVersion](#setumbracoversion)
* [Build](#build)
* [Build-UmbracoDocs](#build-umbracodocs)
* [Verify-NuGet](#verify-nuget)
* [Cleaning up](#cleaning-up)
[Azure DevOps](#azure-devops)
[Quirks](#quirks)
* [Powershell quirks](#powershell-quirks)
* [Git quirks](#git-quirks)
## Building from source
Did you read ["Are you sure"](#are-you-sure)?
### Quick!
To build Umbraco, fire up PowerShell and move to Umbraco's repository root (the directory that contains `src`, `build`, `LICENSE.md`...). There, trigger the build with the following command:
build/build.ps1
## PowerShell Quirks
You might run into [Powershell quirks](#powershell-quirks).
### Build Infrastructure
The Umbraco Build infrastructure relies on a PowerShell object. The object can be retrieved with:
$ubuild = build/build.ps1 -get
The object exposes various properties and methods that can be used to fine-grain build Umbraco. Some, but not all, of them are detailed below.
#### Properties
The object exposes the following properties:
* `SolutionRoot`: the absolute path to the solution root
* `VisualStudio`: a Visual Studio object (see below)
* `NuGet`: the absolute path to the NuGet executable
* `Zip`: the absolute path to the 7Zip executable
* `VsWhere`: the absolute path to the VsWhere executable
* `NodePath`: the absolute path to the Node install
* `NpmPath`: the absolute path to the Npm install
The Visual Studio object is `null` when Visual Studio has not been detected (eg on VSTS). When not null, the object exposes the following properties:
* `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
#### GetUmbracoVersion
Gets an object representing the current Umbraco version. Example:
$v = $ubuild.GetUmbracoVersion()
Write-Host $v.Semver
The object exposes the following properties:
* `Semver`: the semver object representing the version
* `Release`: the main part of the version (eg `7.6.33`)
* `Comment`: the pre release part of the version (eg `alpha02`)
* `Build`: the build number part of the version (eg `1234`)
#### SetUmbracoVersion
Modifies Umbraco files with the new version.
>This entirely replaces the legacy `UmbracoVersion.txt` file. Do *not* edit version infos in files.
The version must be a valid semver version. It can include a *pre release* part (eg `alpha02`) and/or a *build number* (eg `1234`). Examples:
$ubuild.SetUmbracoVersion("7.6.33")
$ubuild.SetUmbracoVersion("7.6.33-alpha.2")
$ubuild.SetUmbracoVersion("7.6.33+1234")
$ubuild.SetUmbracoVersion("7.6.33-beta.5+5678")
#### Build
Builds Umbraco. Temporary files are generated in `build.tmp` while the actual artifacts (zip files, NuGet packages...) are produced in `build.out`. Example:
$ubuild.Build()
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
**Note: web.config**
Building Umbraco requires a clean `web.config` file in the `Umbraco.Web.UI` project. If a `web.config` file already exists, the `pre-build` task (see below) will save it as `web.config.temp-build` and replace it with a clean copy of `web.Template.config`. The original file is replaced once it is safe to do so, by the `pre-packages` task.
#### Build-UmbracoDocs
Builds umbraco documentation. Temporary files are generated in `build.tmp` while the actual artifacts (docs...) are produced in `build.out`. Example:
Build-UmbracoDocs
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
#### Verify-NuGet
Verifies that projects all require the same version of their dependencies, and that NuSpec files require versions that are consistent with projects. Example:
Verify-NuGet
### Cleaning up
Once the solution has been used to run a site, one may want to "reset" the solution in order to run a fresh new site again.
At the very minimum, you want
git clean -Xdf src/Umbraco.Web.UI/App_Data
rm src/Umbraco.Web.UI/web.config
Then, a simple 'Rebuild All' in Visual Studio will recreate a fresh `web.config` but should be quite fast (since it does not really need to rebuild anything).
The `clean` Git command force (`-f`) removes (`-X`, note the capital X) all files and directories (`-d`) that are ignored by Git.
This will leave media files and views around, but in most cases, it will be enough.
To perform a more complete clear, you will want to also delete the content of the media, views, scripts... directories.
The following command will force remove all untracked files and directories, whether they are ignored by Git or not. Combined with `git reset` it can recreate a pristine working directory.
git clean -xdf .
For git documentation see:
* git [clean](<https://git-scm.com/docs/git-clean>)
* git [reset](<https://git-scm.com/docs/git-reset>)
## Azure DevOps
Umbraco uses Azure DevOps for continuous integration, nightly builds and release builds. The Umbraco CMS project on DevOps [is available for anonymous users](https://umbraco.visualstudio.com/Umbraco%20Cms).
DevOps uses the `Build-Umbraco` command several times, each time passing a different *target* parameter. The supported targets are:
* `pre-build`: prepares the build
* `compile-belle`: compiles Belle
* `compile-umbraco`: compiles Umbraco
* `pre-tests`: prepares the tests
* `compile-tests`: compiles the tests
* `pre-packages`: prepares the packages
* `pkg-zip`: creates the zip files
* `pre-nuget`: prepares NuGet packages
* `pkg-nuget`: creates NuGet packages
All these targets are executed when `Build-Umbraco` is invoked without a parameter (or with the `all` parameter). On VSTS, compilations (of Umbraco and tests) are performed by dedicated DevOps tasks. Similarly, creating the NuGet packages is also performed by dedicated DevOps tasks.
Finally, the produced artifacts are published in two containers that can be downloaded from DevOps: `zips` contains the zip files while `nuget` contains the NuGet packages.
>During a DevOps build, some environment `UMBRACO_*` variables are exported by the `pre-build` target and can be reused in other targets *and* in DevOps tasks. The `UMBRACO_TMP` environment variable is used in `Umbraco.Tests` to disable some tests that have issues with DevOps at the moment.
## Quirks
### PowerShell Quirks
There is a good chance that running `build.ps1` ends up in error, with messages such as
@@ -45,120 +207,6 @@ The best solution is to unblock the Zip file before un-zipping: right-click the
PS> Get-ChildItem -Recurse *.* | Unblock-File
## Git Quirks
### 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).
# Build Infrastructure
The Umbraco Build infrastructure relies on a PowerShell object. The object can be retrieved with:
$ubuild = build/build.ps1 -get
The object exposes various properties and methods that can be used to fine-grain build Umbraco. Some, but not all, of them are detailed below.
## Properties
The object exposes the following properties:
* `SolutionRoot`: the absolute path to the solution root
* `VisualStudio`: a Visual Studio object (see below)
* `NuGet`: the absolute path to the NuGet executable
* `Zip`: the absolute path to the 7Zip executable
* `VsWhere`: the absolute path to the VsWhere executable
* `NodePath`: the absolute path to the Node install
* `NpmPath`: the absolute path to the Npm install
The Visual Studio object is `null` when Visual Studio has not been detected (eg on VSTS). When not null, the object exposes the following properties:
* `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
## GetUmbracoVersion
Gets an object representing the current Umbraco version. Example:
$v = $ubuild.GetUmbracoVersion()
Write-Host $v.Semver
The object exposes the following properties:
* `Semver`: the semver object representing the version
* `Release`: the main part of the version (eg `7.6.33`)
* `Comment`: the pre release part of the version (eg `alpha02`)
* `Build`: the build number part of the version (eg `1234`)
## SetUmbracoVersion
Modifies Umbraco files with the new version.
>This entirely replaces the legacy `UmbracoVersion.txt` file. Do *not* edit version infos in files.
The version must be a valid semver version. It can include a *pre release* part (eg `alpha02`) and/or a *build number* (eg `1234`). Examples:
$ubuild.SetUmbracoVersion("7.6.33")
$ubuild.SetUmbracoVersion("7.6.33-alpha.2")
$ubuild.SetUmbracoVersion("7.6.33+1234")
$ubuild.SetUmbracoVersion("7.6.33-beta.5+5678")
## Build
Builds Umbraco. Temporary files are generated in `build.tmp` while the actual artifacts (zip files, NuGet packages...) are produced in `build.out`. Example:
$ubuild.Build()
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
### web.config
Building Umbraco requires a clean `web.config` file in the `Umbraco.Web.UI` project. If a `web.config` file already exists, the `pre-build` task (see below) will save it as `web.config.temp-build` and replace it with a clean copy of `web.Template.config`. The original file is replaced once it is safe to do so, by the `pre-packages` task.
## Build-UmbracoDocs
Builds umbraco documentation. Temporary files are generated in `build.tmp` while the actual artifacts (docs...) are produced in `build.out`. Example:
Build-UmbracoDocs
Some log files, such as MsBuild logs, are produced in `build.tmp` too. The `build` directory should remain clean during a build.
## Verify-NuGet
Verifies that projects all require the same version of their dependencies, and that NuSpec files require versions that are consistent with projects. Example:
Verify-NuGet
# VSTS
Continuous integration, nightly builds and release builds run on VSTS.
VSTS uses the `Build-Umbraco` command several times, each time passing a different *target* parameter. The supported targets are:
* `pre-build`: prepares the build
* `compile-belle`: compiles Belle
* `compile-umbraco`: compiles Umbraco
* `pre-tests`: prepares the tests
* `compile-tests`: compiles the tests
* `pre-packages`: prepares the packages
* `pkg-zip`: creates the zip files
* `pre-nuget`: prepares NuGet packages
* `pkg-nuget`: creates NuGet packages
All these targets are executed when `Build-Umbraco` is invoked without a parameter (or with the `all` parameter). On VSTS, compilations (of Umbraco and tests) are performed by dedicated VSTS tasks. Similarly, creating the NuGet packages is also performed by dedicated VSTS tasks.
Finally, the produced artifacts are published in two containers that can be downloaded from VSTS: `zips` contains the zip files while `nuget` contains the NuGet packages.
>During a VSTS build, some environment `UMBRACO_*` variables are exported by the `pre-build` target and can be reused in other targets *and* in VSTS tasks. The `UMBRACO_TMP` environment variable is used in `Umbraco.Tests` to disable some tests that have issues with VSTS at the moment.
# Notes
*This part needs to be cleaned up*
Nightlies should use some sort of build number.
We should increment versions as soon as a version is released. Ie, as soon as `7.6.33` is released, we should `Set-UmbracoVersion 7.6.34-alpha` and push.
NuGet / NuSpec consistency checks are performed in tests. We should move it so it is done as part of the PowerShell script even before we try to compile and run the tests.
/eof
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).
-34
View File
@@ -1,34 +0,0 @@
Umbraco Cms Clear
--
----
Once the solution has been used to run a site, one may want to "reset" the solution in order to run a fresh new site again.
## Fast
At the very minimum, you want
git clean -Xdf src/Umbraco.Web.UI/App_Data
rm src/Umbraco.Web.UI/web.config
Then, a simple 'Rebuild All' in Visual Studio will recreate a fresh `web.config` but should be quite fast (since it does not really need to rebuild anything).
The `clean` Git command force (`-f`) removes (`-X`, note the capital X) all files and directories (`-d`) that are ignored by Git.
This will leave medias and views around, but in most cases, it will be enough.
## More
To perform a more complete clear, you will want to also delete the content of the media, views, masterpages, scripts... directories.
## Full
The following command will force remove all untracked files and directories, be they ignored by Git or not. Combined with `git reset` it can recreate a pristine working directory.
git clean -xdf .
## Docs
See
* git [clean](<https://git-scm.com/docs/git-clean>)
* git [reset](<https://git-scm.com/docs/git-reset>)
+108 -18
View File
@@ -1,4 +1,3 @@
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/dev-v8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_
# Contributing to Umbraco CMS
👍🎉 First off, thanks for taking the time to contribute! 🎉👍
@@ -9,12 +8,35 @@ These are mostly guidelines, not rules. Use your best judgment, and feel free to
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).
**Table of contents**
[Contributing code changes](#contributing-code-changes)
* [Guidelines for contributions we welcome](#guidelines-for-contributions-we-welcome)
* [What can I start with?](#what-can-i-start-with)
* [How do I begin?](#how-do-i-begin)
* [Pull requests](#pull-requests)
[Reviews](#reviews)
* [Styleguides](#styleguides)
* [The PR team](#the-pr-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)
* [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)
## Contributing code changes
This document gives you a quick overview on how to get started, we will link to in-depth documentation throughout if you need some more background info.
This document gives you a quick overview on how to get started.
## Guidelines for contributions we welcome
### 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.
@@ -22,7 +44,11 @@ We have [documented what we consider small and large changes](CONTRIBUTION_GUIDE
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.
## How do I begin?
### What can I start with?
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` issues](https://github.com/umbraco/Umbraco-CMS/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3Acommunity%2Fup-for-grabs+)
### How do I begin?
Great question! The short version goes like this:
@@ -36,17 +62,23 @@ Great question! The short version goes like this:
* **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md)
* **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions)
* **Commit** - done? Yay! 🎉 It is recommended to create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345`
* **Commit** - done? Yay! 🎉 **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 `dev-v8`, 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 a pull request](img/createpullrequest.png)
### Further reading
### Pull requests
The most successful pull requests usually look a like this:
At this point you might want to [read on about contributing in depth](CONTRIBUTING_DETAILED.md).
* Fill in the required template
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.com/documentation/Reference/) is generated
### Reviews
Again, these are guidelines, not strict requirements.
## Reviews
You've sent us your first contribution, congratulations! Now what?
@@ -59,13 +91,13 @@ We have [a process in place which you can read all about](REVIEW_PROCESS.md). Th
- 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
## Styleguides
### Styleguides
To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
## The PR team
### The PR 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
@@ -76,22 +108,80 @@ The pull request team consists of a member of Umbraco HQ, [Sebastiaan](https://g
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.
## Questions?
### 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:
- 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
## Code of Conduct
## Working with the code
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).
### Building Umbraco from source code
In order to build the Umbraco source code locally, first make sure you have the following installed.
## Contributing to Umbraco, in depth
* Visual Studio 2017 v15.9.7+
* Node v10+
* npm v6.4.1+
There are other ways to contribute, and there's a few more things that you might be wondering about. We will answer the [most common questions and ways to contribute in our detailed documentation](CONTRIBUTING_DETAILED.md).
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.
## Credits
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.
This contribution guide borrows heavily from the excellent work on [the Atom contribution guide](https://github.com/atom/atom/blob/master/CONTRIBUTING.md). A big [#h5yr](http://h5yr.com/) to them!
![Gulp build in Visual Studio](img/gulpbuild.png)
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:
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:
```
npm cache clean --force
npm install
npm run build
```
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.
* [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 `dev-v8`. Whatever the default is, that's where we'd like you to target your contributions.
![Which branch should I target?](img/defaultbranch.png)
### Making changes after the PR was opened
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.
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.
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
```
Then when you want to get the changes from the main repository:
```
git fetch upstream
git rebase upstream/dev-v8
```
In this command we're syncing with the `dev-v8` 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))
-160
View File
@@ -1,160 +0,0 @@
# Contributing in detail
There's more than one way to contribute to Umbraco, there's some more suggestions below.
When contributing code to Umbraco there's plenty of things you'll want to know, skip down to [What should I know before I get started](#what-should-i-know-before-i-get-started) for the answers to your burning questions.
#### Table Of Contents
[How Can I Contribute?](#how-can-i-contribute)
* [Reporting Bugs](#reporting-bugs)
* [Suggesting Enhancements](#suggesting-enhancements)
* [Your First Code Contribution](#your-first-code-contribution)
* [Pull Requests](#pull-requests)
[Styleguides](#styleguides)
[What should I know before I get started?](#what-should-i-know-before-i-get-started)
* [Working with the source code](#working-with-the-source-code)
* [What branch should I target for my contributions?](#what-branch-should-i-target-for-my-contributions)
* [Building Umbraco from source code](#building-umbraco-from-source-code)
* [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository)
## How Can I Contribute?
### Reporting Bugs
This section guides you through submitting a bug report for Umbraco CMS. Following these guidelines helps maintainers and the community understand your report 📝, reproduce the behavior 💻 💻, and find related reports 🔎.
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](https://github.com/umbraco/Umbraco-CMS/issues/new/choose), the information it asks for helps us resolve issues faster.
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
##### Before Submitting A Bug Report
* Most importantly, check **if you can reproduce the problem** in the [latest version of Umbraco](https://our.umbraco.com/download/). We might have already fixed your particular problem.
* It also helps tremendously to check if the issue you're experiencing is present in **a clean install** of the Umbraco version you're currently using. Custom code can have side-effects that don't occur in a clean install.
* **Use the Google**. Whatever you're experiencing, Google it plus "Umbraco" - usually you can get some pretty good hints from the search results, including open issues and further troubleshooting hints.
* If you do find and existing issue has **and the issue is still open**, add a comment to the existing issue if you have additional information. If you have the same problem and no new info to add, just "star" the issue.
Explain the problem and include additional details to help maintainers reproduce the problem. The following is a long description which we've boiled down into a few very simple questions in the issue tracker when you create a new issue. We're listing the following hints to indicate that the most successful reports usually have a lot of this ground covered:
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining which steps you took in the backoffice to get to a certain undesireable result, e.g. you created a document type, inherting 3 levels deep, added a certain datatype, tried to save it and you got an error.
* **Provide specific examples to demonstrate the steps**. If you wrote some code, try to provide a code sample as specific as possible to be able to reproduce the behavior.
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
Provide more context by answering these questions:
* **Can you reproduce the problem** when `debug="false"` in your `web.config` file?
* **Did the problem start happening recently** (e.g. after updating to a new version of Umbraco) or was this always a problem?
* **Can you reliably reproduce the issue?** If not, provide details about how often the problem happens and under which conditions it normally happens.
Include details about your configuration and environment:
* **Which version of Umbraco are you using?**
* **What is the environment you're using Umbraco in?** Is this a problem on your local machine or on a server. Tell us about your configuration: Windows version, IIS/IISExpress, database type, etc.
* **Which packages do you have installed?**
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for Umbraco, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion 📝 and find related suggestions 🔎.
Most of the suggestions in the [reporting bugs](#reporting-bugs) section also count for suggesting enhancements.
Some additional hints that may be helpful:
* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of Umbraco which the suggestion is related to.
* **Explain why this enhancement would be useful to most Umbraco users** and isn't something that can or should be implemented as a [community package](https://our.umbraco.com/projects/).
### Your First Code Contribution
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` and issues](https://issues.umbraco.org/issues?q=&project=U4&tagValue=upforgrabs&release=&issueType=&search=search) or on the [new issue tracker](https://github.com/umbraco/Umbraco-CMS/issues?q=is%3Aopen+is%3Aissue+label%3Acommunity%2Fup-for-grabs).
### Pull Requests
The most successful pull requests usually look a like this:
* Fill in the required template
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.com/documentation/Reference/) is generated
Again, these are guidelines, not strict requirements.
## Making changes after the PR was opened
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!
## Styleguides
To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
## What should I know before I get started?
### Working with the source code
Some parts of our source code is 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:
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:
```
npm cache clean --force
npm install
npm run build
```
2. "The rest" is a C# based codebase, with some traces of our WebForms past but mostly ASP.NET MVC based these days. 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.
* [The AngularJS based backoffice files](https://our.umbraco.com/apidocs/ui/#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
* [The rest](https://our.umbraco.com/apidocs/csharp/)
### What 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 `dev-v7`. Whatever the default is, that's where we'd like you to target your contributions.
![What branch do you want me to target?](img/defaultbranch.png)
### Building Umbraco from source code
In order to build the Umbraco source code locally, first make sure you have the following installed.
* Visual Studio 2017 v15.3+
* Node v10+ (Installed via `build.bat` script. If you already have it installed, make sure you're running at least v10)
* npm v6.4.1+ (Installed via `build.bat` script. If you already have it installed, make sure you're running at least v6.4.1)
The easiest way to get started is to run `build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`. See [this page](BUILD.md) for more details.
Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 (version 15.3 or higher, [the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile.
![Gulp build in Visual Studio](img/gulpbuild.png)
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.
### 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.
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.
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
```
Then when you want to get the changes from the main repository:
```
git fetch upstream
git rebase upstream/dev-v7
```
In this command we're syncing with the `dev-v7` branch, but you can of course choose another one if needed.
(More info on how this works: [http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated](http://robots.thoughtbot.com/post/5133345960/keeping-a-git-fork-updated))
-33
View File
@@ -1,33 +0,0 @@
# Contributing to Umbraco CMS
When youre 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.
Were 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. These issues are usually small enough to fit in the "Small PRs" category and we encourage anyone to pick them up and help out.
If you do start working on something, make sure to leave a small comment on the issue saying something like: "I'm working on this". That way other people stumbling upon the issue know they don't need to pick it up, someone already has.
## Large PRs
New features and large refactorings - can be recognized by seeing a large number of changes, plenty of new files, updates to package manager files (NuGets packages.config, NPMs packages.json, etc.).
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 dont 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 wed 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, well get feedback to you 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. Well keep you posted on the progress.
### Pull request or package?
If it doesnt fit in CMS right now, we will likely encourage you to make it into a package instead. A package is a great way to check out popularity of a feature, learn how people use it, validate good usability and to fix bugs.
Eventually, a package could "graduate" to be included in the CMS.
+10 -1
View File
@@ -5,8 +5,17 @@
If there's an existing issue for this PR then this fixes <!-- link to the issue here! -->
### Description
<!-- A description of the changes proposed in the pull-request and how to test these changes -->
<!--
A description of the changes proposed in the pull-request and how to test these changes.
The most successful pull requests usually look a like this:
* Fill in this template with details: what did you do, why did you do it, how can we test the changes?
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
While these are guidelines and not strict requirements, they really help us evaluate your PR quicker.
-->
<!-- Thanks for contributing to Umbraco CMS! -->
+23 -47
View File
@@ -1,64 +1,40 @@
[![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=dev-v8)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
# [Umbraco CMS](https://umbraco.com) &middot; [![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](../LICENSE.md) [![Build status](https://umbraco.visualstudio.com/Umbraco%20Cms/_apis/build/status/Cms%208%20Continuous?branchName=dev-v8)](https://umbraco.visualstudio.com/Umbraco%20Cms/_build?definitionId=75) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](CONTRIBUTING.md) [![pullreminders](https://pullreminders.com/badge.svg)](https://pullreminders.com?ref=badge)
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.
_You are browsing the Umbraco v8 branch. Umbraco 8 is currently under development._
Learn more at [umbraco.com](https://umbraco.com)
_Looking for Umbraco version 7? [Click here](https://github.com/umbraco/Umbraco-CMS/tree/dev-v7) to go to the v7 branch._
<p align="center">
<img src="img/logo.png" alt="Umbraco Logo" />
</p>
_Ready to try out Version 8? [See the quick start guide](V8_GETTING_STARTED.md)._
See the official [Umbraco website](https://umbraco.com) for an introduction, core mission and values of the product and team behind it.
When is Umbraco 8 coming?
=========================
When it's ready. We're done with the major parts of the architecture work and are focusing on three separate tracks to prepare Umbraco 8 for release:
1) Editor Track (_currently in progress_). Without editors, there's no market for Umbraco. So we want to make sure that Umbraco 8 is full of love for editors.
2) Partner Track. Without anyone implementing Umbraco, there's nothing for editors to update. So we want to make sure that Umbraco 8 is a joy to implement
3) Contributor Track. Without our fabulous ecosystem of both individual Umbracians and 3rd party ISVs, Umbraco wouldn't be as rich a platform as it is today. We want to make sure that it's easy, straight forward and as backwards-compatible as possible to create packages for Umbraco
- [Getting Started](#getting-started)
- [Documentation](#documentation)
- [Community](#join-the-umbraco-community)
- [Contributing](#contributing)
Once a track is done, we start releasing previews where we ask people to test the features we believe are ready. While the testing is going on and we gather feedback, we move on to the next track. This doesn't mean that there hasn't already been work done in the area, but that a track focuses on finalizing, polishing and preparing the features for release.
Please also see our [Code of Conduct](CODE_OF_CONDUCT.md).
Umbraco CMS
===========
The friendliest, most flexible and fastest growing ASP.NET CMS, and used by more than 443,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
[![ScreenShot](img/vimeo.png)](https://vimeo.com/172382998/)
## Umbraco CMS
Umbraco is a free open source Content Management System built on the ASP.NET platform. Our mission is to help you deliver delightful digital experiences by making Umbraco friendly, simpler and social.
## Watch an introduction video
[![ScreenShot](https://shop.umbraco.com/images/whatisumbraco.png)](https://umbraco.tv/videos/umbraco-v7/content-editor/basics/introduction/cms-explanation/)
## Umbraco - The Friendly CMS
For the first time on the Microsoft platform, there is a free user- and developer-friendly CMS that makes it quick and easy to create websites - and a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, right out of the box.
Umbraco is not only loved by developers, but is a content editor's dream. Enjoy intuitive editing tools, media management, responsive views, and approval workflows to send your content live.
Used by more than 443,000 active websites including Carlsberg, Segway, Amazon and Heinz and **The Official ASP.NET and IIS.NET website from Microsoft** ([https://asp.net](https://asp.net) / [https://iis.net](https://iis.net)), you can be sure that the technology is proven, stable and scalable. Backed by the team at Umbraco HQ, and supported by a dedicated community of over 220,000 craftspeople globally, you can trust that Umbraco is a safe choice and is here to stay.
To view more examples, please visit [https://umbraco.com/case-studies-testimonials/](https://umbraco.com/case-studies-testimonials/)
## Why Open Source?
As an Open Source platform, Umbraco is more than just a CMS. We are transparent with our roadmap for future versions, our incremental sprint planning notes are publicly accessible, and community contributions and packages are available for all to use.
## Trying out Umbraco CMS
## Getting Started
[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, you can [download Umbraco]((https://our.umbraco.com/download)) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host it yourself, and handling deployments and upgrades will be all up to you.
## Community
## Documentation
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.
The documentation for Umbraco CMS can be found [on Our Umbraco](https://our.umbraco.com/documentation/). The source for the Umbraco docs is [open source as well](https://github.com/umbraco/UmbracoDocs) and we're happy to look at your documentation contributions.
## Contribute to Umbraco
## Join the Umbraco community
Umbraco is contribution-focused and community-driven. If you want to contribute back to Umbraco, please check out our [guide to contributing](CONTRIBUTING.md).
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.
## Found a bug?
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)
Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report refer to our [online guide for reporting issues](CONTRIBUTING_DETAILED.md#reporting-bugs).
You can comment and report issues on the [github issue tracker](https://github.com/umbraco/Umbraco-CMS/issues).
Since [September 2018](https://umbraco.com/blog/a-second-take-on-umbraco-issue-tracker-hello-github-issues/), the old issue tracker is in read-only mode, but can still be found at [http://issues.umbraco.org](http://issues.umbraco.org).
## Contributing
Umbraco is contribution-focused and community-driven. If you want to contribute back to the Umbraco source code, please check out our [guide to contributing](CONTRIBUTING.md).
-25
View File
@@ -1,25 +0,0 @@
# 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 weve seen your PR and well pick it up as soon as we can.
You will get feedback within at most 14 days after opening the PR. Youll 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, well give you feedback on the changes wed like to see
- Your proposed change is awesome but.. not something were looking to include at this point. Well close your PR and the related issue (well 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 were asking for your help to improve the PR well wait for two weeks to give you a fair chance to make changes. Well ask for an update if we dont hear back from you after that time.
If we dont hear back from you for 4 weeks, well close the PR so that it doesnt just hang around forever. Youre 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 well finish the final improvements wed like to see ourselves. You still get the credits and your commits will live on in the git repository.
-37
View File
@@ -1,37 +0,0 @@
## A quick start guide for getting up and runnning with Umbraco v8
### What you need:
* [Visual Studio 2017 Community (Free)](https://www.visualstudio.com/vs/community/), or Professional, Enterprise, etc... _(**Minimum of Version 15.7** or higher, this is important, you WILL get issues with lesser versions)_
* .NET Framework 4.7.2 installed, get it here: https://www.microsoft.com/net/download/thank-you/net472?survey=false
* .NET Framework 4.7.2 developer pack, get it here: https://www.microsoft.com/net/download/thank-you/net472-developer-pack _(be sure this is the ENU file which will be named `NDP472-DevPack-ENU.exe`)_
* Clone the Umbraco repository using the `dev-v8` branch. If your git client doesn't support specifying the branch as you clone then use the command `git clone --single-branch -b dev-v8 <your fork url>`. _(If you clone the repo using the default v7 branch and then checkout the `dev-v8` branch you **might** get problems)_
### Start the solution
* Open the `/src/umbraco.sln` Visual Studio solution
* Start the solution (easiest way is to use `ctrl + F5`)
* When the solution is first built this may take some time since it will restore all nuget and npm packages, build the .net solution and also build the angular solution
* When the website starts you'll see the Umbraco installer and just follow the prompts
* You're all set!
### Want to run from a zip instead?
If you just want to try out a few things, you can run the site from a zip file which you can download from here https://github.com/umbraco/Umbraco-CMS/releases/tag/temp8-cg18.
We recommend running the site with the Visual Studio since you'll be able to remain up to date with the latest source code changes.
### Making code changes
* _[The process for making code changes in v8 is the same as v7](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md)_
* Any .NET changes you make you just need to compile
* Any Angular/JS changes you make you will need to make sure you are running the Gulp build. Easiest way to do this is from within Visual Studio in the `Task Runner Explorer`. You can find this window by pressing `ctrl + q` and typing in `Task Runner Explorer`. In this window you'll see all Gulp tasks, double click on the `dev` task, this will compile the angular solution and start a file watcher, then any html/js changes you make are automatically built.
* When making js changes, you should have the chrome developer tools open to ensure that cache is disabled
* If you are only making C# changes and are not touching the UI code at all, you can significantly speed up the VS build by adding an empty file specifically called `~/src/preserve.belle`. The UI (Belle) build will then be skipped during a Rebuild.
### What to work on?
We are keeping track of [known issues and limitations here](http://issues.umbraco.org/issue/U4-11279). These line items will eventually be turned into actual tasks to be worked on. Feel free to help us keep this list updated if you find issues and even help fix some of these items. If there is a particular item you'd like to help fix please mention this on the task and we'll create a sub task for the item to continue discussion there.
There's [a list of tasks for v8 that haven't been completed](https://github.com/umbraco/Umbraco-CMS/labels/release%2F8.0.0). If you are interested in helping out with any of these please mention this on the task. This list will be constantly updated as we begin to document and design some of the other tasks that still need to get done.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="4.6.0">
<metadata minClientVersion="4.1.0">
<id>UmbracoCms.Core</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="4.6.0">
<metadata minClientVersion="4.1.0">
<id>UmbracoCms.Web</id>
<version>8.0.0</version>
<title>Umbraco Cms Core Binaries</title>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
<metadata minClientVersion="4.6.0">
<metadata minClientVersion="4.1.0">
<id>UmbracoCms</id>
<version>8.0.0</version>
<title>Umbraco Cms</title>
+8 -7
View File
@@ -1,12 +1,13 @@
_ _ __ __ ____ _____ _____ ____
| | | | \/ | _ \| __ \ /\ / ____/ __ \
| | | | \ / | |_) | |__) | / \ | | | | | |
| | | | |\/| | _ <| _ / / /\ \| | | | | |
| |__| | | | | |_) | | \ \ / ____ | |___| |__| |
\____/|_| |_|____/|_| \_/_/ \_\_____\____/
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"
----------------------------------------------------
------------------------------------------------------------------
Don't forget to build!
@@ -22,7 +22,7 @@ namespace Umbraco.Core.Compose
private static void ContentService_Moved(IContentService sender, MoveEventArgs<IContent> e)
{
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContent.ToInvariantString())))
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinContentString)))
{
var relationService = Current.Services.RelationService;
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentDocumentOnDeleteAlias;
@@ -37,7 +37,7 @@ namespace Umbraco.Core.Compose
private static void MediaService_Moved(IMediaService sender, MoveEventArgs<IMedia> e)
{
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMedia.ToInvariantString())))
foreach (var item in e.MoveInfoCollection.Where(x => x.OriginalPath.Contains(Constants.System.RecycleBinMediaString)))
{
var relationService = Current.Services.RelationService;
const string relationTypeAlias = Constants.Conventions.RelationTypes.RelateParentMediaFolderOnDeleteAlias;
@@ -98,7 +98,6 @@ namespace Umbraco.Core.Composing.CompositionExtensions
: appPlugins.GetDirectories()
.SelectMany(x => x.GetDirectories("Lang"))
.SelectMany(x => x.GetFiles("*.xml", SearchOption.TopDirectoryOnly))
.Where(x => Path.GetFileNameWithoutExtension(x.FullName).Length == 5)
.Select(x => new LocalizedTextServiceSupplementaryFileSource(x, false));
//user defined langs that overwrite the default, these should not be used by plugin creators
@@ -106,7 +105,6 @@ namespace Umbraco.Core.Composing.CompositionExtensions
? Enumerable.Empty<LocalizedTextServiceSupplementaryFileSource>()
: configLangFolder
.GetFiles("*.user.xml", SearchOption.TopDirectoryOnly)
.Where(x => Path.GetFileNameWithoutExtension(x.FullName).Length == 10)
.Select(x => new LocalizedTextServiceSupplementaryFileSource(x, true));
return new LocalizedTextServiceFileSources(
+22
View File
@@ -203,6 +203,28 @@ namespace Umbraco.Core
composition.RegisterUnique(_ => registrar);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="factory">A function creating the options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this Composition composition, Func<IFactory, DatabaseServerMessengerOptions> factory)
{
composition.RegisterUnique(factory);
}
/// <summary>
/// Sets the database server messenger options.
/// </summary>
/// <param name="composition">The composition.</param>
/// <param name="options">Options.</param>
/// <remarks>Use DatabaseServerRegistrarAndMessengerComposer.GetDefaultOptions to get the options that Umbraco would use by default.</remarks>
public static void SetDatabaseServerMessengerOptions(this Composition composition, DatabaseServerMessengerOptions options)
{
composition.RegisterUnique(_ => options);
}
/// <summary>
/// Sets the short string helper.
/// </summary>
@@ -93,7 +93,6 @@ namespace Umbraco.Core
/// </summary>
public const string DisableElectionForSingleServer = "Umbraco.Core.DisableElectionForSingleServer";
/// <summary>
/// Debug specific web.config AppSetting keys for Umbraco
/// </summary>
+2 -2
View File
@@ -145,11 +145,11 @@
public const string PartialViewMacros = "partialViewMacros";
public const string LogViewer = "logViewer";
public const string LogViewer = "logViewer";
public static class Groups
{
public const string Settings = "settingsGroup";
public const string Settings = "settingsGroup";
public const string Templating = "templatingGroup";
+66 -66
View File
@@ -92,10 +92,10 @@ namespace Umbraco.Core
/// </summary>
public const string Extension = "umbracoExtension";
/// <summary>
/// The default height/width of an image file if the size can't be determined from the metadata
/// </summary>
public const int DefaultSize = 200;
/// <summary>
/// The default height/width of an image file if the size can't be determined from the metadata
/// </summary>
public const int DefaultSize = 200;
}
/// <summary>
@@ -209,71 +209,71 @@ namespace Umbraco.Core
public static Dictionary<string, PropertyType> GetStandardPropertyTypeStubs()
{
return new Dictionary<string, PropertyType>
{
{
Comments,
new PropertyType(PropertyEditors.Aliases.TextArea, ValueStorageType.Ntext, true, Comments)
{
Comments,
new PropertyType(PropertyEditors.Aliases.TextArea, ValueStorageType.Ntext, true, Comments)
{
Name = CommentsLabel
}
},
{
FailedPasswordAttempts,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Integer, true, FailedPasswordAttempts)
{
Name = FailedPasswordAttemptsLabel
}
},
{
IsApproved,
new PropertyType(PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, true, IsApproved)
{
Name = IsApprovedLabel
}
},
{
IsLockedOut,
new PropertyType(PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, true, IsLockedOut)
{
Name = IsLockedOutLabel
}
},
{
LastLockoutDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate)
{
Name = LastLockoutDateLabel
}
},
{
LastLoginDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate)
{
Name = LastLoginDateLabel
}
},
{
LastPasswordChangeDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate)
{
Name = LastPasswordChangeDateLabel
}
},
{
PasswordAnswer,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer)
{
Name = PasswordAnswerLabel
}
},
{
PasswordQuestion,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion)
{
Name = PasswordQuestionLabel
}
Name = CommentsLabel
}
};
},
{
FailedPasswordAttempts,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Integer, true, FailedPasswordAttempts)
{
Name = FailedPasswordAttemptsLabel
}
},
{
IsApproved,
new PropertyType(PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, true, IsApproved)
{
Name = IsApprovedLabel
}
},
{
IsLockedOut,
new PropertyType(PropertyEditors.Aliases.Boolean, ValueStorageType.Integer, true, IsLockedOut)
{
Name = IsLockedOutLabel
}
},
{
LastLockoutDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLockoutDate)
{
Name = LastLockoutDateLabel
}
},
{
LastLoginDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastLoginDate)
{
Name = LastLoginDateLabel
}
},
{
LastPasswordChangeDate,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Date, true, LastPasswordChangeDate)
{
Name = LastPasswordChangeDateLabel
}
},
{
PasswordAnswer,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordAnswer)
{
Name = PasswordAnswerLabel
}
},
{
PasswordQuestion,
new PropertyType(PropertyEditors.Aliases.Label, ValueStorageType.Nvarchar, true, PasswordQuestion)
{
Name = PasswordQuestionLabel
}
}
};
}
}
+1 -11
View File
@@ -1,17 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Umbraco.Core
namespace Umbraco.Core
{
public static partial class Constants
{
public static class Icons
{
/// <summary>
/// System contenttype icon
/// </summary>
@@ -42,12 +34,10 @@ namespace Umbraco.Core
/// </summary>
public const string MemberType = "icon-users";
/// <summary>
/// System member icon
/// </summary>
public const string Template = "icon-layout";
}
}
}
+1 -4
View File
@@ -1,7 +1,4 @@
using System;
using System.ComponentModel;
namespace Umbraco.Core
namespace Umbraco.Core
{
public static partial class Constants
{
@@ -123,7 +123,6 @@ namespace Umbraco.Core
public static readonly Guid Template = new Guid(Strings.Template);
public static readonly Guid ContentItem = new Guid(Strings.ContentItem);
}
}
}
@@ -3,7 +3,7 @@
public static partial class Constants
{
/// <summary>
/// Defines the constants used for the Umbraco package repository
/// Defines the constants used for the Umbraco package repository
/// </summary>
public static class PackageRepository
{
@@ -34,7 +34,6 @@ namespace Umbraco.Core
/// </summary>
public const string ContentPicker = "Umbraco.ContentPicker";
/// <summary>
/// DateTime.
/// </summary>
+1 -5
View File
@@ -1,7 +1,4 @@
using System;
using System.ComponentModel;
namespace Umbraco.Core
namespace Umbraco.Core
{
public static partial class Constants
{
@@ -22,7 +19,6 @@ namespace Umbraco.Core
public const string PreviewCookieName = "UMB_PREVIEW";
public const string InstallerCookieName = "umb_installId";
}
}
}
+11 -5
View File
@@ -7,6 +7,7 @@ using System.Web;
using System.Xml.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPoco.Expressions;
using Umbraco.Core.Composing;
using Umbraco.Core.IO;
using Umbraco.Core.Models;
@@ -52,8 +53,8 @@ namespace Umbraco.Core
return ContentStatus.Unpublished;
}
#endregion
/// <summary>
@@ -134,9 +135,14 @@ namespace Umbraco.Core
/// <summary>
/// Sets the posted file value of a property.
/// </summary>
/// <remarks>This really is for FileUpload fields only, and should be obsoleted. For anything else,
/// you need to store the file by yourself using Store and then figure out
/// how to deal with auto-fill properties (if any) and thumbnails (if any) by yourself.</remarks>
public static void SetValue(this IContentBase content, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, HttpPostedFileBase postedFile, string culture = null, string segment = null)
{
content.SetValue(contentTypeBaseServiceProvider, propertyTypeAlias, postedFile.FileName, postedFile.InputStream, culture, segment);
}
/// <summary>
/// Sets the posted file value of a property.
/// </summary>
public static void SetValue(this IContentBase content, IContentTypeBaseServiceProvider contentTypeBaseServiceProvider, string propertyTypeAlias, string filename, Stream filestream, string culture = null, string segment = null)
{
if (filename == null || filestream == null) return;
+5 -5
View File
@@ -151,22 +151,22 @@ namespace Umbraco.Core.Models
internal static bool HasContentRootAccess(this IUser user, IEntityService entityService)
{
return ContentPermissionsHelper.HasPathAccess(Constants.System.Root.ToInvariantString(), user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
return ContentPermissionsHelper.HasPathAccess(Constants.System.RootString, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
}
internal static bool HasContentBinAccess(this IUser user, IEntityService entityService)
{
return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinContent.ToInvariantString(), user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinContentString, user.CalculateContentStartNodeIds(entityService), Constants.System.RecycleBinContent);
}
internal static bool HasMediaRootAccess(this IUser user, IEntityService entityService)
{
return ContentPermissionsHelper.HasPathAccess(Constants.System.Root.ToInvariantString(), user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
return ContentPermissionsHelper.HasPathAccess(Constants.System.RootString, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
}
internal static bool HasMediaBinAccess(this IUser user, IEntityService entityService)
{
return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinMedia.ToInvariantString(), user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
return ContentPermissionsHelper.HasPathAccess(Constants.System.RecycleBinMediaString, user.CalculateMediaStartNodeIds(entityService), Constants.System.RecycleBinMedia);
}
internal static bool HasPathAccess(this IUser user, IContent content, IEntityService entityService)
@@ -327,7 +327,7 @@ namespace Umbraco.Core.Models
? entityService.GetAllPaths(objectType, asn).ToDictionary(x => x.Id, x => x.Path)
: new Dictionary<int, string>();
paths[Constants.System.Root] = Constants.System.Root.ToString(); // entityService does not get that one
paths[Constants.System.Root] = Constants.System.RootString; // entityService does not get that one
var binPath = GetBinPath(objectType);
@@ -69,7 +69,7 @@ namespace Umbraco.Core
public const string Tag = /*TableNamePrefix*/ "cms" + "Tags";
public const string TagRelationship = /*TableNamePrefix*/ "cms" + "TagRelationship";
public const string KeyValue = TableNamePrefix + "KeyValue";
public const string AuditEntry = TableNamePrefix + "Audit";
@@ -292,7 +292,6 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
if (psql.Arguments[i] is string s && s == "[[[ISOCODE]]]")
{
psql.Arguments[i] = ordering.Culture;
break;
}
}
}
@@ -61,7 +61,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// move to parent (or -1), update path, save
moving.ParentId = parentId;
var movingPath = moving.Path + ","; // save before changing
moving.Path = (container == null ? Constants.System.Root.ToString() : container.Path) + "," + moving.Id;
moving.Path = (container == null ? Constants.System.RootString : container.Path) + "," + moving.Id;
moving.Level = container == null ? 1 : container.Level + 1;
Save(moving);
@@ -232,7 +232,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
.OrderByDescending<ContentVersionDto>(x => x.Current)
.AndByDescending<ContentVersionDto>(x => x.VersionDate);
return MapDtosToContent(Database.Fetch<DocumentDto>(sql), true, true);
return MapDtosToContent(Database.Fetch<DocumentDto>(sql), true, true).Skip(skip).Take(take);
}
public override IContent GetVersion(int versionId)
@@ -982,8 +982,14 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// invariant: left join will yield NULL and we must use pcv to determine published
// variant: left join may yield NULL or something, and that determines published
var joins = Sql()
.InnerJoin<ContentTypeDto>("ctype").On<ContentDto, ContentTypeDto>((content, contentType) => content.ContentTypeId == contentType.NodeId, aliasRight: "ctype");
.InnerJoin<ContentTypeDto>("ctype").On<ContentDto, ContentTypeDto>((content, contentType) => content.ContentTypeId == contentType.NodeId, aliasRight: "ctype")
// left join on optional culture variation
//the magic "[[[ISOCODE]]]" parameter value will be replaced in ContentRepositoryBase.GetPage() by the actual ISO code
.LeftJoin<ContentVersionCultureVariationDto>(nested =>
nested.InnerJoin<LanguageDto>("langp").On<ContentVersionCultureVariationDto, LanguageDto>((ccv, lang) => ccv.LanguageId == lang.Id && lang.IsoCode == "[[[ISOCODE]]]", "ccvp", "langp"), "ccvp")
.On<ContentVersionDto, ContentVersionCultureVariationDto>((version, ccv) => version.Id == ccv.VersionId, aliasLeft: "pcv", aliasRight: "ccvp");
sql = InsertJoins(sql, joins);
@@ -993,7 +999,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
// when invariant, ie 'variations' does not have the culture flag (value 1), use the global 'published' flag on pcv.id,
// otherwise check if there's a version culture variation for the lang, via ccv.id
", (CASE WHEN (ctype.variations & 1) = 0 THEN (CASE WHEN pcv.id IS NULL THEN 0 ELSE 1 END) ELSE (CASE WHEN ccv.id IS NULL THEN 0 ELSE 1 END) END) AS ordering "); // trailing space is important!
", (CASE WHEN (ctype.variations & 1) = 0 THEN (CASE WHEN pcv.id IS NULL THEN 0 ELSE 1 END) ELSE (CASE WHEN ccvp.id IS NULL THEN 0 ELSE 1 END) END) AS ordering "); // trailing space is important!
sql = Sql(sqlText, sql.Arguments);
@@ -2,39 +2,61 @@
namespace Umbraco.Core.Scoping
{
// base class for an object that will be enlisted in scope context, if any. it
// must be used in a 'using' block, and if not scoped, released when disposed,
// else when scope context runs enlisted actions
/// <summary>
/// Provides a base class for scope contextual objects.
/// </summary>
/// <remarks>
/// <para>A scope contextual object is enlisted in the current scope context,
/// if any, and released when the context exists. It must be used in a 'using'
/// block, and will be released when disposed, if not part of a scope.</para>
/// </remarks>
public abstract class ScopeContextualBase : IDisposable
{
private bool _using, _scoped;
private bool _scoped;
/// <summary>
/// Gets a contextual object.
/// </summary>
/// <typeparam name="T">The type of the object.</typeparam>
/// <param name="scopeProvider">A scope provider.</param>
/// <param name="key">A context key for the object.</param>
/// <param name="ctor">A function producing the contextual object.</param>
/// <returns>The contextual object.</returns>
/// <remarks>
/// <para></para>
/// </remarks>
public static T Get<T>(IScopeProvider scopeProvider, string key, Func<bool, T> ctor)
where T : ScopeContextualBase
{
// no scope context = create a non-scoped object
var scopeContext = scopeProvider.Context;
if (scopeContext == null)
return ctor(false);
// create & enlist the scoped object
var w = scopeContext.Enlist("ScopeContextualBase_" + key,
() => ctor(true),
(completed, item) => { item.Release(completed); });
if (w._using) throw new InvalidOperationException("panic: used.");
w._using = true;
w._scoped = true;
return w;
}
/// <inheritdoc />
/// <remarks>
/// <para>If not scoped, then this releases the contextual object.</para>
/// </remarks>
public void Dispose()
{
_using = false;
if (_scoped == false)
Release(true);
}
/// <summary>
/// Releases the contextual object.
/// </summary>
/// <param name="completed">A value indicating whether the scoped operation completed.</param>
public abstract void Release(bool completed);
}
}
@@ -533,7 +533,7 @@ namespace Umbraco.Core.Services.Implement
//null check otherwise we get exceptions
if (content.Path.IsNullOrWhiteSpace()) return Enumerable.Empty<IContent>();
var rootId = Constants.System.Root.ToInvariantString();
var rootId = Constants.System.RootString;
var ids = content.Path.Split(',')
.Where(x => x != rootId && x != content.Id.ToString(CultureInfo.InvariantCulture)).Select(int.Parse).ToArray();
if (ids.Any() == false)
@@ -1922,7 +1922,7 @@ namespace Umbraco.Core.Services.Implement
// if uow is not immediate, content.Path will be updated only when the UOW commits,
// and because we want it now, we have to calculate it by ourselves
//paths[content.Id] = content.Path;
paths[content.Id] = (parent == null ? (parentId == Constants.System.RecycleBinContent ? "-1,-20" : "-1") : parent.Path) + "," + content.Id;
paths[content.Id] = (parent == null ? (parentId == Constants.System.RecycleBinContent ? "-1,-20" : Constants.System.RootString) : parent.Path) + "," + content.Id;
const int pageSize = 500;
var page = 0;
@@ -184,8 +184,12 @@ namespace Umbraco.Core.Services.Implement
//now load in supplementary
var found = _supplementFileSources.Where(x =>
{
var fileName = Path.GetFileName(x.File.FullName);
return fileName.InvariantStartsWith(culture.Name) && fileName.InvariantEndsWith(".xml");
var extension = Path.GetExtension(x.File.FullName);
var fileCultureName = Path.GetFileNameWithoutExtension(x.File.FullName).Replace("_", "-").Replace(".user", "");
return extension.InvariantEquals(".xml") && (
fileCultureName.InvariantEquals(culture.Name)
|| fileCultureName.InvariantEquals(culture.TwoLetterISOLanguageName)
);
});
foreach (var supplementaryFile in found)
@@ -203,7 +207,7 @@ namespace Umbraco.Core.Services.Implement
continue;
}
if (xChildDoc.Root == null) continue;
if (xChildDoc.Root == null || xChildDoc.Root.Name != "language") continue;
foreach (var xArea in xChildDoc.Root.Elements("area")
.Where(x => ((string)x.Attribute("alias")).IsNullOrWhiteSpace() == false))
{
@@ -995,7 +995,7 @@ namespace Umbraco.Core.Services.Implement
// if uow is not immediate, content.Path will be updated only when the UOW commits,
// and because we want it now, we have to calculate it by ourselves
//paths[media.Id] = media.Path;
paths[media.Id] = (parent == null ? (parentId == Constants.System.RecycleBinMedia ? "-1,-21" : "-1") : parent.Path) + "," + media.Id;
paths[media.Id] = (parent == null ? (parentId == Constants.System.RecycleBinMedia ? "-1,-21" : Constants.System.RootString) : parent.Path) + "," + media.Id;
const int pageSize = 500;
var page = 0;
+1 -1
View File
@@ -20,7 +20,7 @@ namespace Umbraco.Core
internal static Dictionary<string, UdiType> GetTypes()
{
return new Dictionary<string,UdiType>
return new Dictionary<string, UdiType>
{
{ Unknown, UdiType.Unknown },
+6 -6
View File
@@ -23,21 +23,21 @@ namespace Umbraco.Core
/// <returns></returns>
/// <remarks>
/// There are some special routes we need to check to properly determine this:
///
///
/// If any route has an extension in the path like .aspx = back office
///
///
/// These are def back office:
/// /Umbraco/BackOffice = back office
/// /Umbraco/Preview = back office
/// If it's not any of the above, and there's no extension then we cannot determine if it's back office or front-end
/// so we can only assume that it is not back office. This will occur if people use an UmbracoApiController for the backoffice
/// but do not inherit from UmbracoAuthorizedApiController and do not use [IsBackOffice] attribute.
///
///
/// These are def front-end:
/// /Umbraco/Surface = front-end
/// /Umbraco/Api = front-end
/// But if we've got this far we'll just have to assume it's front-end anyways.
///
///
/// </remarks>
internal static bool IsBackOfficeRequest(this Uri url, string applicationPath, IGlobalSettings globalSettings)
{
@@ -152,9 +152,9 @@ namespace Umbraco.Core
var toInclude = new[] {".aspx", ".ashx", ".asmx", ".axd", ".svc"};
return toInclude.Any(ext.InvariantEquals) == false;
}
catch (ArgumentException ex)
catch (ArgumentException)
{
Current.Logger.Error(typeof(UriExtensions), ex, "Failed to determine if request was client side");
Current.Logger.Debug(typeof(UriExtensions), "Failed to determine if request was client side (invalid chars in path \"{Path}\"?)", url.LocalPath);
return false;
}
}
+214 -28
View File
@@ -5,6 +5,7 @@ using Moq;
using NUnit.Framework;
using Umbraco.Core.Scoping;
using Umbraco.Web.PublishedCache.NuCache;
using Umbraco.Web.PublishedCache.NuCache.Snap;
namespace Umbraco.Tests.Cache
{
@@ -388,8 +389,7 @@ namespace Umbraco.Tests.Cache
// collect liveGen
GC.Collect();
SnapDictionary<int, string>.GenerationObject genObj;
Assert.IsTrue(d.Test.GenerationObjects.TryPeek(out genObj));
Assert.IsTrue(d.Test.GenObjs.TryPeek(out var genObj));
genObj = null;
// in Release mode, it works, but in Debug mode, the weak reference is still alive
@@ -399,14 +399,14 @@ namespace Umbraco.Tests.Cache
GC.Collect();
#endif
Assert.IsTrue(d.Test.GenerationObjects.TryPeek(out genObj));
Assert.IsFalse(genObj.WeakReference.IsAlive); // snapshot is gone, along with its reference
Assert.IsTrue(d.Test.GenObjs.TryPeek(out genObj));
Assert.IsFalse(genObj.WeakGenRef.IsAlive); // snapshot is gone, along with its reference
await d.CollectAsync();
Assert.AreEqual(0, d.Test.GetValues(1).Length); // null value is gone
Assert.AreEqual(0, d.Count); // item is gone
Assert.AreEqual(0, d.Test.GenerationObjects.Count);
Assert.AreEqual(0, d.Test.GenObjs.Count);
Assert.AreEqual(0, d.SnapCount); // snapshot is gone
Assert.AreEqual(0, d.GenCount); // and generation has been dequeued
}
@@ -632,7 +632,7 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(1, d.Test.LiveGen);
Assert.IsTrue(d.Test.NextGen);
using (d.GetWriter(GetScopeProvider()))
using (d.GetScopedWriteLock(GetScopeProvider()))
{
var s1 = d.CreateSnapshot();
@@ -685,7 +685,7 @@ namespace Umbraco.Tests.Cache
Assert.IsFalse(d.Test.NextGen);
Assert.AreEqual("uno", s2.Get(1));
using (d.GetWriter(GetScopeProvider()))
using (d.GetScopedWriteLock(GetScopeProvider()))
{
// gen 3
Assert.AreEqual(2, d.Test.GetValues(1).Length);
@@ -712,16 +712,102 @@ namespace Umbraco.Tests.Cache
}
[Test]
public void NestedWriteLocking()
public void NestedWriteLocking1()
{
var d = new SnapDictionary<int, string>();
var t = d.Test;
t.CollectAuto = false;
Assert.AreEqual(0, d.CreateSnapshot().Gen);
// no scope context: writers nest, last one to be disposed commits
var scopeProvider = GetScopeProvider();
using (var w1 = d.GetScopedWriteLock(scopeProvider))
{
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(1, t.WLocked);
Assert.IsTrue(t.NextGen);
using (var w2 = d.GetScopedWriteLock(scopeProvider))
{
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(2, t.WLocked);
Assert.IsTrue(t.NextGen);
Assert.AreNotSame(w1, w2); // get a new writer each time
d.Set(1, "one");
Assert.AreEqual(0, d.CreateSnapshot().Gen);
}
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(1, t.WLocked);
Assert.IsTrue(t.NextGen);
Assert.AreEqual(0, d.CreateSnapshot().Gen);
}
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(0, t.WLocked);
Assert.IsTrue(t.NextGen);
Assert.AreEqual(1, d.CreateSnapshot().Gen);
}
[Test]
public void NestedWriteLocking2()
{
var d = new SnapDictionary<int, string>();
d.Test.CollectAuto = false;
var scopeProvider = GetScopeProvider();
using (d.GetWriter(scopeProvider))
Assert.AreEqual(0, d.CreateSnapshot().Gen);
// scope context: writers enlist
var scopeContext = new ScopeContext();
var scopeProvider = GetScopeProvider(scopeContext);
using (var w1 = d.GetScopedWriteLock(scopeProvider))
{
using (d.GetWriter(scopeProvider))
using (var w2 = d.GetScopedWriteLock(scopeProvider))
{
Assert.AreSame(w1, w2);
d.Set(1, "one");
}
}
}
[Test]
public void NestedWriteLocking3()
{
var d = new SnapDictionary<int, string>();
var t = d.Test;
t.CollectAuto = false;
Assert.AreEqual(0, d.CreateSnapshot().Gen);
var scopeContext = new ScopeContext();
var scopeProvider1 = GetScopeProvider();
var scopeProvider2 = GetScopeProvider(scopeContext);
using (var w1 = d.GetScopedWriteLock(scopeProvider1))
{
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(1, t.WLocked);
Assert.IsTrue(t.NextGen);
using (var w2 = d.GetScopedWriteLock(scopeProvider2))
{
Assert.AreEqual(1, t.LiveGen);
Assert.AreEqual(2, t.WLocked);
Assert.IsTrue(t.NextGen);
Assert.AreNotSame(w1, w2);
d.Set(1, "one");
}
}
@@ -764,7 +850,7 @@ namespace Umbraco.Tests.Cache
var scopeProvider = GetScopeProvider();
using (d.GetWriter(scopeProvider))
using (d.GetScopedWriteLock(scopeProvider))
{
// gen 3
Assert.AreEqual(2, d.Test.GetValues(1).Length);
@@ -809,7 +895,7 @@ namespace Umbraco.Tests.Cache
var scopeProvider = GetScopeProvider();
using (d.GetWriter(scopeProvider))
using (d.GetScopedWriteLock(scopeProvider))
{
// creating a snapshot in a write-lock does NOT return the "current" content
// it uses the previous snapshot, so new snapshot created only on release
@@ -846,9 +932,10 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(2, s2.Gen);
Assert.AreEqual("uno", s2.Get(1));
var scopeProvider = GetScopeProvider(true);
var scopeContext = new ScopeContext();
var scopeProvider = GetScopeProvider(scopeContext);
using (d.GetWriter(scopeProvider))
using (d.GetScopedWriteLock(scopeProvider))
{
// creating a snapshot in a write-lock does NOT return the "current" content
// it uses the previous snapshot, so new snapshot created only on release
@@ -867,7 +954,7 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(2, s4.Gen);
Assert.AreEqual("uno", s4.Get(1));
((ScopeContext) scopeProvider.Context).ScopeExit(true);
scopeContext.ScopeExit(true);
var s5 = d.CreateSnapshot();
Assert.AreEqual(3, s5.Gen);
@@ -878,7 +965,8 @@ namespace Umbraco.Tests.Cache
public void ScopeLocking2()
{
var d = new SnapDictionary<int, string>();
d.Test.CollectAuto = false;
var t = d.Test;
t.CollectAuto = false;
// gen 1
d.Set(1, "one");
@@ -891,12 +979,13 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(2, s2.Gen);
Assert.AreEqual("uno", s2.Get(1));
var scopeProviderMock = new Mock<IScopeProvider>();
var scopeContext = new ScopeContext();
scopeProviderMock.Setup(x => x.Context).Returns(scopeContext);
var scopeProvider = scopeProviderMock.Object;
Assert.AreEqual(2, t.LiveGen);
Assert.IsFalse(t.NextGen);
using (d.GetWriter(scopeProvider))
var scopeContext = new ScopeContext();
var scopeProvider = GetScopeProvider(scopeContext);
using (d.GetScopedWriteLock(scopeProvider))
{
// creating a snapshot in a write-lock does NOT return the "current" content
// it uses the previous snapshot, so new snapshot created only on release
@@ -905,18 +994,35 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual(2, s3.Gen);
Assert.AreEqual("uno", s3.Get(1));
// we made some changes, so a next gen is required
Assert.AreEqual(3, t.LiveGen);
Assert.IsTrue(t.NextGen);
Assert.AreEqual(1, t.WLocked);
// but live snapshot contains changes
var ls = d.Test.LiveSnapshot;
var ls = t.LiveSnapshot;
Assert.AreEqual("ein", ls.Get(1));
Assert.AreEqual(3, ls.Gen);
}
// nothing is committed until scope exits
Assert.AreEqual(3, t.LiveGen);
Assert.IsTrue(t.NextGen);
Assert.AreEqual(1, t.WLocked);
// no changes until exit
var s4 = d.CreateSnapshot();
Assert.AreEqual(2, s4.Gen);
Assert.AreEqual("uno", s4.Get(1));
scopeContext.ScopeExit(false);
// now things have changed
Assert.AreEqual(2, t.LiveGen);
Assert.IsFalse(t.NextGen);
Assert.AreEqual(0, t.WLocked);
// no changes since not completed
var s5 = d.CreateSnapshot();
Assert.AreEqual(2, s5.Gen);
Assert.AreEqual("uno", s5.Get(1));
@@ -955,12 +1061,92 @@ namespace Umbraco.Tests.Cache
Assert.AreEqual("four", all[3]);
}
private IScopeProvider GetScopeProvider(bool withContext = false)
[Test]
public void DontPanic()
{
var scopeProviderMock = new Mock<IScopeProvider>();
var scopeContext = withContext ? new ScopeContext() : null;
scopeProviderMock.Setup(x => x.Context).Returns(scopeContext);
var scopeProvider = scopeProviderMock.Object;
var d = new SnapDictionary<int, string>();
d.Test.CollectAuto = false;
Assert.IsNull(d.Test.GenObj);
// gen 1
d.Set(1, "one");
Assert.IsTrue(d.Test.NextGen);
Assert.AreEqual(1, d.Test.LiveGen);
Assert.IsNull(d.Test.GenObj);
var s1 = d.CreateSnapshot();
Assert.IsFalse(d.Test.NextGen);
Assert.AreEqual(1, d.Test.LiveGen);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(1, d.Test.GenObj.Gen);
Assert.AreEqual(1, s1.Gen);
Assert.AreEqual("one", s1.Get(1));
d.Set(1, "uno");
Assert.IsTrue(d.Test.NextGen);
Assert.AreEqual(2, d.Test.LiveGen);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(1, d.Test.GenObj.Gen);
var scopeContext = new ScopeContext();
var scopeProvider = GetScopeProvider(scopeContext);
// scopeProvider.Context == scopeContext -> writer is scoped
// writer is scope contextual and scoped
// when disposed, nothing happens
// when the context exists, the writer is released
using (d.GetScopedWriteLock(scopeProvider))
{
d.Set(1, "ein");
Assert.IsTrue(d.Test.NextGen);
Assert.AreEqual(3, d.Test.LiveGen);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(2, d.Test.GenObj.Gen);
}
// writer has not released
Assert.AreEqual(1, d.Test.WLocked);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(2, d.Test.GenObj.Gen);
// nothing changed
Assert.IsTrue(d.Test.NextGen);
Assert.AreEqual(3, d.Test.LiveGen);
// panic!
var s2 = d.CreateSnapshot();
Assert.AreEqual(1, d.Test.WLocked);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(2, d.Test.GenObj.Gen);
Assert.AreEqual(3, d.Test.LiveGen);
Assert.IsTrue(d.Test.NextGen);
// release writer
scopeContext.ScopeExit(true);
Assert.AreEqual(0, d.Test.WLocked);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(2, d.Test.GenObj.Gen);
Assert.AreEqual(3, d.Test.LiveGen);
Assert.IsTrue(d.Test.NextGen);
var s3 = d.CreateSnapshot();
Assert.AreEqual(0, d.Test.WLocked);
Assert.IsNotNull(d.Test.GenObj);
Assert.AreEqual(3, d.Test.GenObj.Gen);
Assert.AreEqual(3, d.Test.LiveGen);
Assert.IsFalse(d.Test.NextGen);
}
private IScopeProvider GetScopeProvider(ScopeContext scopeContext = null)
{
var scopeProvider = Mock.Of<IScopeProvider>();
Mock.Get(scopeProvider)
.Setup(x => x.Context).Returns(scopeContext);
return scopeProvider;
}
}
@@ -10,6 +10,7 @@ using Umbraco.Web;
using Umbraco.Web.PublishedCache;
using Umbraco.Core.Composing;
using Moq;
using Newtonsoft.Json;
using Umbraco.Core.Cache;
using Umbraco.Core.Configuration;
using Umbraco.Core.Logging;
@@ -32,6 +33,8 @@ namespace Umbraco.Tests.PublishedContent
protected override void Compose()
{
base.Compose();
_publishedSnapshotAccessorMock = new Mock<IPublishedSnapshotAccessor>();
Composition.RegisterUnique<IPublishedSnapshotAccessor>(_publishedSnapshotAccessorMock.Object);
Composition.RegisterUnique<IPublishedModelFactory>(f => new PublishedModelFactory(f.GetInstance<TypeLoader>().GetTypes<PublishedContentModel>()));
Composition.RegisterUnique<IPublishedContentTypeFactory, PublishedContentTypeFactory>();
@@ -87,6 +90,7 @@ namespace Umbraco.Tests.PublishedContent
}
private readonly Guid _node1173Guid = Guid.NewGuid();
private Mock<IPublishedSnapshotAccessor> _publishedSnapshotAccessorMock;
protected override string GetXmlContent(int templateId)
{
@@ -792,6 +796,91 @@ namespace Umbraco.Tests.PublishedContent
Assert.IsTrue(customDoc3.IsDescendantOrSelf(customDoc3));
}
[Test]
public void SiblingsAndSelf()
{
// Structure:
// - Root : 1046 (no parent)
// -- Level1.1: 1173 (parent 1046)
// --- Level1.1.1: 1174 (parent 1173)
// --- Level1.1.2: 117 (parent 1173)
// --- Level1.1.3: 1177 (parent 1173)
// --- Level1.1.4: 1178 (parent 1173)
// --- Level1.1.5: 1176 (parent 1173)
// -- Level1.2: 1175 (parent 1046)
// -- Level1.3: 4444 (parent 1046)
var root = GetNode(1046);
var level1_1 = GetNode(1173);
var level1_1_1 = GetNode(1174);
var level1_1_2 = GetNode(117);
var level1_1_3 = GetNode(1177);
var level1_1_4 = GetNode(1178);
var level1_1_5 = GetNode(1176);
var level1_2 = GetNode(1175);
var level1_3 = GetNode(4444);
_publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root});
CollectionAssertAreEqual(new []{root}, root.SiblingsAndSelf());
CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_1.SiblingsAndSelf());
CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_2.SiblingsAndSelf());
CollectionAssertAreEqual( new []{level1_1, level1_2, level1_3}, level1_3.SiblingsAndSelf());
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_1.SiblingsAndSelf());
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_2.SiblingsAndSelf());
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_3.SiblingsAndSelf());
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_4.SiblingsAndSelf());
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_5.SiblingsAndSelf());
}
[Test]
public void Siblings()
{
// Structure:
// - Root : 1046 (no parent)
// -- Level1.1: 1173 (parent 1046)
// --- Level1.1.1: 1174 (parent 1173)
// --- Level1.1.2: 117 (parent 1173)
// --- Level1.1.3: 1177 (parent 1173)
// --- Level1.1.4: 1178 (parent 1173)
// --- Level1.1.5: 1176 (parent 1173)
// -- Level1.2: 1175 (parent 1046)
// -- Level1.3: 4444 (parent 1046)
var root = GetNode(1046);
var level1_1 = GetNode(1173);
var level1_1_1 = GetNode(1174);
var level1_1_2 = GetNode(117);
var level1_1_3 = GetNode(1177);
var level1_1_4 = GetNode(1178);
var level1_1_5 = GetNode(1176);
var level1_2 = GetNode(1175);
var level1_3 = GetNode(4444);
_publishedSnapshotAccessorMock.Setup(x => x.PublishedSnapshot.Content.GetAtRoot()).Returns(new []{root});
CollectionAssertAreEqual(new IPublishedContent[0], root.Siblings());
CollectionAssertAreEqual( new []{level1_2, level1_3}, level1_1.Siblings());
CollectionAssertAreEqual( new []{level1_1, level1_3}, level1_2.Siblings());
CollectionAssertAreEqual( new []{level1_1, level1_2}, level1_3.Siblings());
CollectionAssertAreEqual( new []{ level1_1_2, level1_1_3, level1_1_4, level1_1_5}, level1_1_1.Siblings());
CollectionAssertAreEqual( new []{level1_1_1, level1_1_3, level1_1_4, level1_1_5}, level1_1_2.Siblings());
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_4, level1_1_5}, level1_1_3.Siblings());
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_5}, level1_1_4.Siblings());
CollectionAssertAreEqual( new []{level1_1_1, level1_1_2, level1_1_3, level1_1_4}, level1_1_5.Siblings());
}
private void CollectionAssertAreEqual<T>(IEnumerable<T> expected, IEnumerable<T> actual)
where T: IPublishedContent
{
var e = expected.Select(x => x.Id);
var a = actual.Select(x => x.Id);
CollectionAssert.AreEquivalent(e, a, $"\nExpected:\n{string.Join(", ", e)}\n\nActual:\n{string.Join(", ", a)}");
}
[Test]
public void FragmentProperty()
File diff suppressed because it is too large Load Diff
@@ -116,6 +116,13 @@
function isContentCultureVariant() {
return $scope.content.variants.length > 1;
}
function reload() {
$scope.page.loading = true;
loadContent().then(function() {
$scope.page.loading = false;
});
}
function bindEvents() {
//bindEvents can be called more than once and we don't want to have multiple bound events
@@ -123,13 +130,10 @@
eventsService.unsubscribe(evts[e]);
}
evts.push(eventsService.on("editors.content.reload", function (name, args) {
evts.push(eventsService.on("editors.documentType.saved", function (name, args) {
// if this content item uses the updated doc type we need to reload the content item
if(args && args.node && args.node.key === $scope.content.key) {
$scope.page.loading = true;
loadContent().then(function() {
$scope.page.loading = false;
});
if(args && args.documentType && $scope.content.documentType.id === args.documentType.id) {
reload();
}
}));
@@ -147,7 +147,7 @@
id: documentType.id,
submit: function (model) {
const args = { node: scope.node };
eventsService.emit('editors.content.reload', args);
eventsService.emit("editors.content.reload", args);
editorService.close();
},
close: function () {
@@ -13,7 +13,12 @@ angular.module("umbraco.directives")
// TODO: A lot of the code below should be shared between the grid rte and the normal rte
var promises = [];
//To id the html textarea we need to use the datetime ticks because we can have multiple rte's per a single property alias
// because now we have to support having 2x (maybe more at some stage) content editors being displayed at once. This is because
// we have this mini content editor panel that can be launched with MNTP.
scope.textAreaHtmlId = scope.uniqueId + "_" + String.CreateGuid();
//queue file loading
if (typeof (tinymce) === "undefined") {
promises.push(assetsService.loadJs("lib/tinymce/tinymce.min.js", scope));
@@ -34,7 +39,7 @@ angular.module("umbraco.directives")
var tinyMceEditor = null;
promises.push(tinyMceService.getTinyMceEditorConfig({
htmlId: scope.uniqueId,
htmlId: scope.textAreaHtmlId,
stylesheets: editorConfig.stylesheets,
toolbar: editorConfig.toolbar,
mode: editorConfig.mode
@@ -145,17 +150,11 @@ angular.module("umbraco.directives")
}
});
//listen for formSubmitting event (the result is callback used to remove the event subscription)
var formSubmittingListener = scope.$on("formSubmitting", function () {
scope.value = tinyMceEditor ? tinyMceEditor.getContent() : null;
});
//when the element is disposed we need to unsubscribe!
// NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom
// element might still be there even after the modal has been hidden.
scope.$on('$destroy', function () {
formSubmittingListener();
eventsService.unsubscribe(tabShownListener);
//ensure we unbind this in case the blur doesn't fire above
$('.umb-panel-body').off('scroll', pinToolbar);
@@ -32,16 +32,27 @@ angular.module("umbraco.directives")
restrict: 'E',
scope:{
key: '@',
tokens: '='
tokens: '=',
watchTokens: '@'
},
replace: true,
link: function (scope, element, attrs) {
var key = scope.key;
var tokens = scope.tokens ? scope.tokens : null;
localizationService.localize(key, tokens).then(function(value){
element.html(value);
scope.text = "";
// A render function to be able to update tokens as values update.
function render() {
element.html(localizationService.tokenReplace(scope.text, scope.tokens || null));
}
localizationService.localize(key).then(function(value){
scope.text = value;
render();
});
if (scope.watchTokens === 'true') {
scope.$watch("tokens", render, true);
}
}
};
})
@@ -117,9 +117,11 @@ Use this directive to generate a list of content items presented as a flexbox gr
};
scope.clickItemName = function(item, $event, $index) {
if(scope.onClickName) {
if(scope.onClickName && !($event.metaKey || $event.ctrlKey)) {
scope.onClickName(item, $event, $index);
$event.preventDefault();
}
$event.stopPropagation();
};
}
@@ -1,13 +1,17 @@
(function() {
'use strict';
function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource, dataTypeHelper, dataTypeResource, $filter, iconHelper, $q, $timeout, notificationsService, localizationService, editorService) {
function GroupsBuilderDirective(contentTypeHelper, contentTypeResource, mediaTypeResource,
dataTypeHelper, dataTypeResource, $filter, iconHelper, $q, $timeout, notificationsService,
localizationService, editorService, eventsService) {
function link(scope, el, attr, ctrl) {
var eventBindings = [];
var validationTranslated = "";
var tabNoSortOrderTranslated = "";
scope.dataTypeHasChanged = false;
scope.sortingMode = false;
scope.toolbar = [];
scope.sortableOptionsGroup = {};
@@ -613,18 +617,44 @@
});
});
}
var unbindModelWatcher = scope.$watch('model', function(newValue, oldValue) {
if (newValue !== undefined && newValue.groups !== undefined) {
activate();
function hasPropertyOfDataTypeId(dataTypeId) {
// look at each property
var result = _.filter(scope.model.groups, function(group) {
return _.filter(group.properties, function(property) {
return (property.dataTypeId === dataTypeId);
});
});
return (result.length > 0);
}
});
// clean up
scope.$on('$destroy', function(){
unbindModelWatcher();
});
eventBindings.push(scope.$watch('model', function(newValue, oldValue) {
if (newValue !== undefined && newValue.groups !== undefined) {
activate();
}
}));
// clean up
eventBindings.push(eventsService.on("editors.dataTypeSettings.saved", function (name, args) {
if(hasPropertyOfDataTypeId(args.dataType.id)) {
scope.dataTypeHasChanged = true;
}
}));
// clean up
eventBindings.push(scope.$on('$destroy', function() {
for(var e in eventBindings) {
eventBindings[e]();
}
// if a dataType has changed, we want to notify which properties that are affected by this dataTypeSettings change
if(scope.dataTypeHasChanged === true) {
var args = {documentType: scope.model};
eventsService.emit("editors.documentType.saved", args);
}
}));
}
@@ -56,7 +56,7 @@ Use this directive make an element sticky and follow the page when scrolling.
}
if (attr.scrollableContainer) {
scrollableContainer = $(attr.scrollableContainer);
scrollableContainer = bar.closest(attr.scrollableContainer);
} else {
scrollableContainer = $(window);
}
@@ -121,7 +121,7 @@ Use this directive make an element sticky and follow the page when scrolling.
function calculateSize() {
var width = bar.innerWidth();
clonedBar.css({
width: width
width: width + 10 // + 10 (5*2) because we need to add border to avoid seeing the shadow beneath. Look at the CSS.
});
}
@@ -117,10 +117,11 @@
var vm = this;
vm.clickItem = function (item, $event) {
if (vm.onClick) {
if (vm.onClick && !($event.metaKey || $event.ctrlKey)) {
vm.onClick({ item: item});
$event.stopPropagation();
$event.preventDefault();
}
$event.stopPropagation();
};
vm.selectItem = function (item, $index, $event) {
@@ -62,8 +62,8 @@ function valPropertyMsg(serverValidationManager) {
if (!watcher) {
watcher = scope.$watch("currentProperty.value",
function (newValue, oldValue) {
if (!newValue || angular.equals(newValue, oldValue)) {
if (angular.equals(newValue, oldValue)) {
return;
}
@@ -78,10 +78,12 @@ function valPropertyMsg(serverValidationManager) {
// based on other errors. We'll also check if there's no other validation errors apart from valPropertyMsg, if valPropertyMsg
// is the only one, then we'll clear.
if ((errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
if (errCount === 0 || (errCount === 1 && angular.isArray(formCtrl.$error.valPropertyMsg)) || (formCtrl.$invalid && angular.isArray(formCtrl.$error.valServer))) {
scope.errorMsg = "";
formCtrl.$setValidity('valPropertyMsg', true);
stopWatch();
} else if (showValidation && scope.errorMsg === "") {
formCtrl.$setValidity('valPropertyMsg', false);
scope.errorMsg = getErrorMsg();
}
}, true);
}
@@ -152,6 +154,7 @@ function valPropertyMsg(serverValidationManager) {
showValidation = true;
if (hasError && scope.errorMsg === "") {
scope.errorMsg = getErrorMsg();
startWatch();
}
else if (!hasError) {
scope.errorMsg = "";
@@ -59,11 +59,11 @@
* Method for internal use, based on the collection of layouts passed, the method selects either
* any previous layout from local storage, or picks the first allowed layout
*
* @param {Number} nodeId The id of the current node displayed in the content editor
* @param {Any} id The identifier of the current node or application displayed in the content editor
* @param {Array} availableLayouts Array of all allowed layouts, available from $scope.model.config.layouts
*/
function getLayout(nodeId, availableLayouts) {
function getLayout(id, availableLayouts) {
var storedLayouts = [];
@@ -74,8 +74,8 @@
if (storedLayouts && storedLayouts.length > 0) {
for (var i = 0; storedLayouts.length > i; i++) {
var layout = storedLayouts[i];
if (layout.nodeId === nodeId) {
return setLayout(nodeId, layout, availableLayouts);
if (isMatchingLayout(id, layout)) {
return setLayout(id, layout, availableLayouts);
}
}
@@ -93,12 +93,12 @@
* @description
* Changes the current layout used by the listview to the layout passed in. Stores selection in localstorage
*
* @param {Number} nodeID Id of the current node displayed in the content editor
* @param {Any} id The identifier of the current node or application displayed in the content editor
* @param {Object} selectedLayout Layout selected as the layout to set as the current layout
* @param {Array} availableLayouts Array of all allowed layouts, available from $scope.model.config.layouts
*/
function setLayout(nodeId, selectedLayout, availableLayouts) {
function setLayout(id, selectedLayout, availableLayouts) {
var activeLayout = {};
var layoutFound = false;
@@ -118,7 +118,7 @@
activeLayout = getFirstAllowedLayout(availableLayouts);
}
saveLayoutInLocalStorage(nodeId, activeLayout);
saveLayoutInLocalStorage(id, activeLayout);
return activeLayout;
@@ -132,11 +132,11 @@
* @description
* Stores a given layout as the current default selection in local storage
*
* @param {Number} nodeId Id of the current node displayed in the content editor
* @param {Any} id The identifier of the current node or application displayed in the content editor
* @param {Object} selectedLayout Layout selected as the layout to set as the current layout
*/
function saveLayoutInLocalStorage(nodeId, selectedLayout) {
function saveLayoutInLocalStorage(id, selectedLayout) {
var layoutFound = false;
var storedLayouts = [];
@@ -147,7 +147,7 @@
if (storedLayouts.length > 0) {
for (var i = 0; storedLayouts.length > i; i++) {
var layout = storedLayouts[i];
if (layout.nodeId === nodeId) {
if (isMatchingLayout(id, layout)) {
layout.path = selectedLayout.path;
layoutFound = true;
}
@@ -156,7 +156,7 @@
if (!layoutFound) {
var storageObject = {
"nodeId": nodeId,
"id": id,
"path": selectedLayout.path
};
storedLayouts.push(storageObject);
@@ -510,6 +510,12 @@
};
}
function isMatchingLayout(id, layout) {
// legacy format uses "nodeId", be sure to look for both
return layout.id === id || layout.nodeId === id;
}
var service = {
getLayout: getLayout,
@@ -43,16 +43,11 @@ angular.module('umbraco.services')
var entry = dictionary[value];
if (entry) {
if (tokens) {
for (var i = 0; i < tokens.length; i++) {
entry = entry.replace("%" + i + "%", tokens[i]);
}
}
return entry;
return service.tokenReplace(entry, tokens);
}
return "[" + value + "]";
}
var service = {
// array to hold the localized resource string entries
dictionary: [],
@@ -127,7 +122,29 @@ angular.module('umbraco.services')
}
return value;
},
/**
* @ngdoc method
* @name umbraco.services.localizationService#tokenReplace
* @methodOf umbraco.services.localizationService
*
* @description
* Helper to replace tokens
* @param {String} value the text-string to manipulate
* @param {Array} tekens An array of tokens values
* @returns {String} Replaced test-string
*/
tokenReplace: function (text, tokens) {
if (tokens) {
for (var i = 0; i < tokens.length; i++) {
text = text.replace("%" + i + "%", tokens[i]);
}
}
return text;
},
/**
* @ngdoc method
* @name umbraco.services.localizationService#localize
@@ -146,8 +163,7 @@ angular.module('umbraco.services')
*/
localize: function (value, tokens) {
return service.initLocalizedResources().then(function (dic) {
var val = _lookup(value, tokens, dic);
return val;
return _lookup(value, tokens, dic);
});
},
@@ -85,17 +85,17 @@ angular.module('umbraco.services')
nArray.push(item);
if(!item.sticky) {
$timeout(function() {
var found = _.find(nArray, function(i) {
return i.id === item.id;
});
if (found) {
var index = nArray.indexOf(found);
nArray.splice(index, 1);
}
}, 7000);
$timeout(
function() {
var found = _.find(nArray, function(i) {
return i.id === item.id;
});
if (found) {
var index = nArray.indexOf(found);
nArray.splice(index, 1);
}
}
, 10000);
}
return item;
@@ -137,6 +137,7 @@
@import "components/umb-iconpicker.less";
@import "components/umb-insert-code-box.less";
@import "components/umb-packages.less";
@import "components/umb-logviewer.less";
@import "components/umb-package-local-install.less";
@import "components/umb-panel-group.less";
@import "components/umb-lightbox.less";
@@ -167,6 +168,7 @@
@import "components/umb-property-file-upload.less";
@import "components/users/umb-user-cards.less";
@import "components/users/umb-user-table.less";
@import "components/users/umb-user-details.less";
@import "components/users/umb-user-group-picker-list.less";
@import "components/users/umb-user-group-preview.less";
@@ -167,7 +167,7 @@ a.umb-editor-header__close-split-view:hover {
text-decoration: none !important;
font-size: 13px;
//color: @gray-4;
color: @ui-action-disgrete-type;
color: @ui-action-discreet-type;
//background-color: @white;
}
@@ -175,15 +175,15 @@ a.umb-variant-switcher__toggle {
transition: color 0.2s ease-in-out;
&:hover {
//background-color: @gray-10;
color: @ui-action-disgrete-type-hover;
color: @ui-action-discreet-type-hover;
.umb-variant-switcher__expand {
color: @ui-action-disgrete-type-hover;
color: @ui-action-discreet-type-hover;
}
}
}
.umb-variant-switcher__expand {
color: @ui-action-disgrete-type;
color: @ui-action-discreet-type;
margin-top: 3px;
margin-left: 5px;
margin-right: -5px;
@@ -195,6 +195,7 @@ a.umb-variant-switcher__toggle {
justify-content: space-between;
align-items: center;
border-bottom: 1px solid @gray-9;
position: relative;
&:hover .umb-variant-switcher__name-wrapper {
}
@@ -246,10 +247,15 @@ a.umb-variant-switcher__toggle {
}
.umb-variant-switcher__split-view {
font-size: 13px;
display: none;
padding: 16px 20px;
font-size: 13px;
display: none;
padding: 16px 20px;
position: absolute;
right: 0;
top: 0;
bottom: 0;
background-color: @white;
&:hover {
background-color: @ui-option-hover;
color: @ui-option-type-hover;
@@ -1,7 +1,11 @@
.umb-editor-sub-header {
padding: 10px 0;
margin-bottom: 10px;
background: @gray-10;
background: @brownGrayLight;
border-left: 5px solid @brownGrayLight;
border-right: 5px solid @brownGrayLight;
margin-left: -5px;
margin-right: -5px;
display: flex;
justify-content: space-between;
margin-top: -10px;
@@ -12,7 +16,7 @@
&.nested {
margin-top: 0;
margin-bottom: 0;
background: @gray-10;
background: @brownGrayLight;
}
}
@@ -25,10 +29,14 @@
.umb-editor-sub-header.-umb-sticky-bar {
box-shadow: 0 6px 3px -3px rgba(0,0,0,.16);
transition: box-shadow 1s;
top: calc(@appHeaderHeight + @editorHeaderHeight);
transition: box-shadow 240ms;
margin-top: 0;
margin-bottom: 0;
top: calc(@appHeaderHeight + @editorHeaderHeight);
.umb-editor--infinityMode & {
top: calc(@editorHeaderHeight);
}
}
.umb-group-builder__property-preview .umb-editor-sub-header {
@@ -22,7 +22,14 @@
font-size: 14px;
border: none;
position: relative;
margin-bottom: 0;
border-radius: 10px;
margin: 10px;
.close {
top: 0;
right: -6px;
opacity: 0.4;
}
}
.umb-notifications__notification.-extra-padding {
@@ -11,4 +11,5 @@
animation: fadeIn;
margin-top: 15px;
pointer-events: none;
border-radius: 3px;
}
@@ -13,24 +13,37 @@
user-select: none;
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.16);
border-radius: 3px;
}
.umb-content-grid__item.-selected {
&::before {
content: "";
position: absolute;
z-index:2;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border: 2px solid @ui-selected-border;
border-radius: 5px;
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
pointer-events: none;
.umb-content-grid__item {
&.-selected, &:hover {
&::before {
content: "";
position: absolute;
z-index: 2;
top: -2px;
left: -2px;
right: -2px;
bottom: -2px;
border: 2px solid @ui-selected-border;
border-radius: 5px;
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
pointer-events: none;
}
}
}
.umb-content-grid__item:hover {
&::before {
opacity: .33;
}
}
.umb-content-grid__item.-selected:hover {
&::before {
opacity: .75;
}
}
.umb-content-grid__icon-container {
@@ -59,7 +72,8 @@
display: inline-flex;
color: @ui-option-type;
&:hover {
&:hover, &:focus {
text-decoration: none;
color:@ui-option-type-hover;
}
}
@@ -41,13 +41,13 @@
// file select link
.file-select {
font-size: 15px;
color: @ui-action-disgrete-type;
color: @ui-action-discreet-type;
cursor: pointer;
margin-top: 10px;
&:hover {
color: @ui-action-disgrete-type-hover;
color: @ui-action-discreet-type-hover;
text-decoration: none;
}
}
@@ -27,7 +27,12 @@
.umb-folder-grid__folder.-selected {
color:@ui-selected-type;
&:hover {
color:@ui-selected-type-hover;
}
}
.umb-folder-grid__folder.-selected, .umb-folder-grid__folder:hover {
&::before {
content: "";
position: absolute;
@@ -41,9 +46,15 @@
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
pointer-events: none;
}
&:hover {
color:@ui-selected-type-hover;
}
.umb-folder-grid__folder:hover {
&::before {
opacity: .33;
}
}
.umb-folder-grid__folder.-selected:hover {
&::before {
opacity: .75;
}
}
@@ -128,9 +128,10 @@
position: relative;
margin-bottom: 40px;
padding-top: 10px;
border: 1px solid @grayLighter;
&:hover {
background-color: @grayLighter;
border-color: @grayLight;
}
&[ng-click],
@@ -161,15 +162,16 @@
}
.umb-grid .umb-row .umb-cell-placeholder {
min-height: 130px;
background-color: @gray-10;
border-width: 2px;
min-height: 88px;
border-width: 1px;
border-style: dashed;
border-color: @gray-8;
border-color: @ui-action-discreet-border;
color: @ui-action-discreet-type;
transition: border-color 100ms linear;
&:hover {
border-color: @blueMid;
border-color: @ui-action-discreet-border-hover;
color: @ui-action-discreet-type-hover;
cursor: pointer;
}
}
@@ -207,9 +209,9 @@
}
.umb-grid .cell-tools-add {
color: @ui-action-type;
color: @ui-action-discreet-type;
&:focus, &:hover {
color: @ui-action-type-hover;
color: @ui-action-discreet-type-hover;
text-decoration: none;
}
}
@@ -219,16 +221,18 @@
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: @ui-action-type;
color: @ui-action-discreet-type;
}
.umb-grid .cell-tools-add.-bar {
display: block;
background: @gray-10;
text-align: center;
padding: 5px;
border: 1px dashed @gray-7;
border: 1px dashed @ui-action-discreet-border;
margin: 10px;
&:focus, &:hover {
border-color: @ui-action-discreet-border-hover;
}
}
@@ -249,7 +253,6 @@
.umb-grid .cell-tools-edit {
display: inline-block;
color: @white;
}
.umb-grid .drop-overlay {
@@ -412,36 +415,17 @@
// Row states
.umb-grid .umb-row.-active {
background-color: @ui-action-type;
border-color: @ui-action-type;
.umb-row-title-bar {
cursor: move;
}
.row-tool,
.umb-row-title {
color: @white;
}
.umb-grid-has-config {
color: fade(@white, 66);
}
.umb-cell {
.umb-grid-has-config {
color: fade(@black, 44);
}
}
.umb-cell .umb-cell-content {
border-color: transparent;
}
}
.umb-grid .umb-row.-active-child {
background-color: @gray-10;
.umb-row-title-bar {
cursor: inherit;
}
@@ -449,22 +433,7 @@
.umb-row-title {
color: @gray-3;
}
.row-tool {
color: fade(@black, 23);
}
.umb-grid-has-config {
color: fade(@black, 44);
}
.umb-cell-content.-placeholder {
border-color: @gray-8;
&:hover {
border-color: fade(@gray, 44);
}
}
}
@@ -573,14 +542,13 @@
display: inline-block;
cursor: pointer;
border-radius: 200px;
background: @gray-10;
border: 1px solid @gray-7;
border: 1px solid @ui-action-discreet-border;
margin: 2px;
&:hover, &:hover * {
background: @ui-action-type-hover !important;
background: @ui-action-discreet-type-hover !important;
color: @white !important;
border-color: @ui-action-type-hover !important;
border-color: @ui-action-discreet-border-hover !important;
text-decoration: none;
}
}
@@ -0,0 +1,42 @@
/* PACKAGE DETAILS */
.umb-logviewer {
display: flex;
flex-flow: row wrap;
}
@sidebarwidth: 350px; // Width of sidebar. Ugly hack because of old version of Less
.umb-logviewer__main-content {
flex: 1 1 auto;
margin-right: 20px;
width: calc(~'100%' - ~'@{sidebarwidth}' - ~'20px'); // Make sure that the main content area doesn't gets affected by inline styling
min-width: 500px;
.btn-link {
text-align: left;
}
}
.umb-logviewer__sidebar {
flex: 0 0 @sidebarwidth;
}
@media (max-width: 768px) {
.umb-logviewer {
flex-direction: column;
}
.umb-logviewer__main-content {
flex: 1 1 auto;
width: 100%;
margin-bottom: 30px;
margin-right: 0;
}
.umb-logviewer__sidebar {
flex: 1 1 auto;
width: 100%;
}
}
@@ -38,12 +38,12 @@
}
.umb-media-grid__item.-selected {
//background: @ui-selected;
color:@ui-selected-type;
//border-color: @ui-selected-border;
//box-shadow: 0 2px 8px 0 darken(@ui-selected-border, 20);
.umb-media-grid__item-overlay {
color: @ui-selected-type;
}
}
.umb-media-grid__item.-selected, .umb-media-grid__item:hover {
&::before {
content: "";
position: absolute;
@@ -57,12 +57,16 @@
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
pointer-events: none;
}
.umb-media-grid__item-overlay {
color: @ui-selected-type;
//background: @ui-selected-border;
}
.umb-media-grid__item:hover {
&::before {
opacity: .33;
}
}
.umb-media-grid__item.-selected:hover {
&::before {
opacity: .75;
}
}
.umb-media-grid__item-file-icon > span {
@@ -158,6 +162,9 @@
.umb-media-grid__item:hover .umb-media-grid__item-overlay {
opacity: 1;
i {
text-decoration: none;
}
}
.umb-media-grid__item-name {
@@ -211,7 +218,7 @@
transition: opacity 150ms;
&:hover {
color: @ui-action-disgrete-type-hover;
color: @ui-action-discreet-type-hover;
}
}
@@ -89,16 +89,16 @@
display: flex;
align-items: center;
justify-content: center;
border: 1px dashed @ui-action-disgrete-border;
color: @ui-action-disgrete-type;
border: 1px dashed @ui-action-discreet-border;
color: @ui-action-discreet-type;
font-weight: bold;
padding: 5px 15px;
box-sizing: border-box;
}
.umb-node-preview-add:hover {
color: @ui-action-disgrete-type-hover;
border-color: @ui-action-disgrete-border-hover;
color: @ui-action-discreet-type-hover;
border-color: @ui-action-discreet-border-hover;
text-decoration: none;
}
@@ -408,7 +408,7 @@ a.umb-package-details__back-link {
.umb-gallery__thumbnail {
flex: 0 1 100px;
border: 1px solid @ui-action-disgrete-border;
border: 1px solid @ui-action-discreet-border;
border-radius: 3px;
margin: 5px;
padding: 10px;
@@ -418,7 +418,7 @@ a.umb-package-details__back-link {
.umb-gallery__thumbnail:hover {
cursor: pointer;
border-color: @ui-action-disgrete-border-hover;
border-color: @ui-action-discreet-border-hover;
}
/* PACKAGE LIST */
@@ -91,12 +91,39 @@ input.umb-table__input {
.umb-table-body .umb-table-row {
color: @gray-5;
border-top: 1px solid @gray-9;
cursor: pointer;
font-size: 14px;
position: relative;
min-height: 52px;
}
.umb-table-body .umb-table-row.-selectable {
cursor: pointer;
}
.umb-table-row.-selected,
.umb-table-body .umb-table-row.-selectable:hover {
&::before {
content: "";
position: absolute;
z-index:1;
top: 1px;
left: 1px;
right: 1px;
bottom: 1px;
border: 2px solid @ui-selected-border;
pointer-events: none;
}
}
.umb-table-body .umb-table-row.-selectable {
&:hover {
background-color: @ui-option-hover;
&::before {
opacity:.33;
}
}
}
.umb-table-body .umb-table-row.-selected.-selectable {
&:hover {
&::before {
opacity:.66;
}
}
}
@@ -122,7 +149,7 @@ input.umb-table__input {
margin: 0 auto;
font-size: 20px;
line-height: 20px;
color: @gray-7;
color: @ui-option-type;
}
.umb-table-body__checkicon,
@@ -135,9 +162,30 @@ input.umb-table__input {
}
.umb-table-body .umb-table__name {
color: @black;
color: @ui-option-type;
font-size: 14px;
font-weight: bold;
a {
color: @ui-option-type;
&:hover, &:focus {
color: @ui-option-type-hover;
text-decoration: underline;
}
}
}
.umb-table-body .umb-table-row.-light {
.umb-table-body__icon {
color: @ui-option-disabled-type;
}
.umb-table__name {
color: @ui-option-disabled-type;
a {
color: @ui-option-disabled-type;
&:hover, &:focus {
color: @ui-option-disabled-type-hover;
}
}
}
}
// Styles of no items in the listview
@@ -150,7 +198,7 @@ input.umb-table__input {
}
// Show checkmark when checked, hide file icon
.umb-table-row--selected {
.umb-table-row.-selected {
.umb-table-body__fileicon {
display: none;
@@ -159,18 +207,6 @@ input.umb-table__input {
display: inline-block;
}
&::before {
content: "";
position: absolute;
z-index:1;
top: 1px;
left: 1px;
right: 1px;
bottom: 1px;
border: 2px solid @ui-selected-border;
box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%);
pointer-events: none;
}
}
// Table Row Styles
@@ -17,7 +17,11 @@
outline: none;
text-decoration: none !important;
}
.umb-user-card.-selected {
.umb-user-card.-selectable {
cursor: pointer;
}
.umb-user-card.-selected, .umb-user-card.-selectable:hover {
&::before {
content: "";
position: absolute;
@@ -26,12 +30,21 @@
left: -2px;
right: -2px;
bottom: -2px;
border: 2px solid @ui-selected-border;
border-radius: 5px;
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
pointer-events: none;
border: 2px solid @ui-selected-border;
box-shadow: 0 0 4px 0 darken(@ui-selected-border, 20), inset 0 0 2px 0 darken(@ui-selected-border, 20);
}
}
.umb-user-card.-selectable:hover {
&::before {
opacity: .33;
}
}
.umb-user-card.-selected:hover {
&::before {
opacity: .75;
}
}
.umb-user-card__content {
@@ -44,14 +57,18 @@
box-sizing: border-box;
display: flex;
flex-direction: column;
cursor: pointer;
max-width: 100%;
}
.umb-user-card__goToUser {
&:hover {
&:hover, &:focus {
text-decoration: none;
.umb-user-card__name {
text-decoration: underline;
text-decoration: underline;
color: @ui-option-type-hover;
}
.umb-avatar {
border: 1px solid @ui-option-type-hover;
}
}
}
@@ -0,0 +1,74 @@
.umb-user-table {
.umb-user-table-col-avatar {
flex: 0 0 32px;
padding: 15px 0;
}
.umb-table-cell a {
&:hover, &:focus {
.umb-avatar {
border: 1px solid @ui-option-type-hover;
}
}
}
.umb-table-body .umb-table-cell.umb-table__name {
margin: 0;
padding: 0;
a {
display: flex;
padding: 6px 2px;
height: 42px;
span {
margin: auto 14px;
}
}
}
.umb-table-cell.umb-table__name a {
&:hover, &:focus {
text-decoration: underline;
}
}
.umb-user-table-row {
.umb-checkmark {
visibility: hidden;
}
}
&.-has-selection {
.umb-user-table-row.-selectable {
.umb-checkmark {
visibility: visible;
}
}
}
.umb-user-table-row.-selectable:hover {
.umb-checkmark {
visibility: visible;
}
}
.umb-user-table-row.-selected {
.umb-checkmark {
visibility: visible;
}
&::before {
content: "";
position: absolute;
z-index:1;
top: 1px;
left: 1px;
right: 1px;
bottom: 1px;
border: 2px solid @ui-selected-border;
box-shadow: 0 0 2px 0 fade(@ui-selected-border, 80%);
pointer-events: none;
}
}
}
@@ -30,7 +30,7 @@
position: absolute;
padding: 5px 8px;
pointer-events: none;
top: 0;
top: 2px;
}
input[type="text"] {
@@ -239,8 +239,8 @@
.list-view-add-layout {
margin-top: 10px;
color: @ui-action-disgrete-type;
border: 1px dashed @ui-action-disgrete-border;
color: @ui-action-discreet-type;
border: 1px dashed @ui-action-discreet-border;
display: flex;
align-items: center;
justify-content: center;
@@ -250,6 +250,6 @@
.list-view-add-layout:hover {
text-decoration: none;
color: @ui-action-disgrete-type-hover;
border-color: @ui-action-disgrete-border-hover;
color: @ui-action-discreet-type-hover;
border-color: @ui-action-discreet-border-hover;
}
+11 -1
View File
@@ -52,7 +52,17 @@
bottom: 0px;
left: 0px;
right: 0px;
position: absolute;;
position: absolute;
}
.--notInFront .umb-modalcolumn::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
background: rgba(0,0,0,.4);
}
/* re-align loader */
@@ -346,7 +346,7 @@
justify-content: center;
align-items: center;
background: @white;
border: 1px solid @ui-action-disgrete-border;
border: 1px solid @ui-action-discreet-border;
animation: fadeIn 0.5s;
border-radius: 3px;
width: 50px;
@@ -354,7 +354,7 @@
.icon {
opacity: 0.8;
}
border-color: @ui-action-disgrete-border-hover;
border-color: @ui-action-discreet-border-hover;
}
}
@@ -16,6 +16,75 @@
border-left: 1px solid @gray-10;
}
.date-wrapper__date .flatpickr-input > a {
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
padding: 4px 15px;
box-sizing: border-box;
min-width: 200px;
color: @ui-action-discreet-type;
border: 1px dashed @ui-action-discreet-border;
border-radius: 3px;
&:hover, &:focus {
text-decoration: none;
color: @ui-action-discreet-type-hover;
border-color: @ui-action-discreet-border-hover;
localize {
text-decoration: none;
}
}
}
//----- VARIANTS SCHEDULED PUBLISH ------
.date-wrapper-mini {
display: flex;
flex-direction: row;
}
.date-wrapper-mini__date {
display: flex;
margin-left: 5px;
margin-top: 5px;
margin-bottom: 10px;
&:first-of-type {
margin-left: 0;
}
}
.date-wrapper-mini__date .flatpickr-input > a {
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
padding: 1px 15px;
box-sizing: border-box;
min-width: 180px;
color: @ui-action-discreet-type;
border: 1px dashed @ui-action-discreet-border;
border-radius: 3px;
&:hover, &:focus {
text-decoration: none;
color: @ui-action-discreet-type-hover;
border-color: @ui-action-discreet-border-hover;
localize {
text-decoration: none;
}
}
}
//------------------- HISTORY ------------------
.history {
@@ -71,4 +140,4 @@
.history-line {
display: none;
}
}
}
@@ -269,8 +269,8 @@
transition: all 150ms ease-in-out;
&:hover {
color: @ui-action-disgrete-type-hover;
border-color: @ui-action-disgrete-type-hover;
color: @ui-action-discreet-type-hover;
border-color: @ui-action-discreet-type-hover;
}
}
@@ -112,7 +112,7 @@
@blueExtraDark: #1b264f;// added 2019
@blueLight: #ADD8E6;
@blueNight: #162335;// added 2019
@orange: #f79c37;// updated 2019
//@orange: #f79c37;// updated 2019
@pink: #D93F4C;// #C3325F;// update 2019
@pinkLight: #f5c1bc;// added 2019
@pinkRedLight: #ff8a89;// added 2019
@@ -130,8 +130,12 @@
// -------------------------
@ui-option-type: @blueExtraDark;
@ui-option-hover: @sand-7;
@ui-option-type-hover: @blueMid;
@ui-option-hover: @sand-7;
@ui-option-disabled-type: @gray-6;
@ui-option-disabled-type-hover: @gray-5;
@ui-option-disabled-hover: @sand-7;
//@ui-active: #346ab3;
@ui-active: @pinkLight;
@@ -162,12 +166,12 @@
@ui-action-border: @blueExtraDark;
@ui-action-border-hover: @blueMid;
@ui-action-disgrete: white;
@ui-action-disgrete-hover: @sand-7;
@ui-action-disgrete-type: @blueExtraDark;
@ui-action-disgrete-type-hover: @blueMid;
@ui-action-disgrete-border: @gray-7;
@ui-action-disgrete-border-hover: @blueMid;
@ui-action-discreet: white;
@ui-action-discreet-hover: @sand-7;
@ui-action-discreet-type: @blueExtraDark;
@ui-action-discreet-type-hover: @blueMid;
@ui-action-discreet-border: @gray-7;
@ui-action-discreet-border-hover: @blueMid;
@type-white: @white;
@type-black: @blueNight;
@@ -195,6 +199,16 @@
.turquoise{color: @turquoise;}
.turquoise-d1{color: @turquoise-d1;}
.text-warning {
color: @orange;
}
.text-error {
color: @red;
}
.text-success {
color: @green;
}
//icon colors for tree icons
.color-red, .color-red i{color: @red-d1 !important;}
@@ -113,12 +113,7 @@ var app = angular.module("umbraco.preview", ['umbraco.resources', 'umbraco.servi
$scope.exitPreview = function () {
var culture = $location.search().culture || getParameterByName("culture");
var relativeUrl = "/" + $scope.pageId;
if(culture){
relativeUrl +='?culture='+ culture;
}
var relativeUrl = "/" + $scope.pageId +'?culture='+ culture;
window.top.location.href = "../preview/end?redir=" + encodeURIComponent(relativeUrl);
};
@@ -10,7 +10,7 @@
(function() {
"use strict";
function DataTypePicker($scope, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) {
function DataTypePicker($scope, $filter, dataTypeResource, dataTypeHelper, contentTypeResource, localizationService, editorService) {
var vm = this;
@@ -119,13 +119,28 @@
$scope.model.itemDetails = null;
if (vm.searchTerm) {
vm.showFilterResult = true;
vm.showTabs = false;
var regex = new RegExp(vm.searchTerm, "i");
vm.filterResult = {
userConfigured: filterCollection(vm.userConfigured, regex),
typesAndEditors: filterCollection(vm.typesAndEditors, regex)
};
} else {
vm.showFilterResult = false;
vm.filterResult = null;
vm.showTabs = true;
}
}
function filterCollection(collection, regex) {
return _.map(_.keys(collection), function (key) {
return {
group: key,
dataTypes: $filter('filter')(collection[key], function (dataType) {
return regex.test(dataType.name) || regex.test(dataType.alias);
})
}
});
}
function showDetailsOverlay(property) {
@@ -201,4 +216,4 @@
angular.module("umbraco").controller("Umbraco.Editors.DataTypePickerController", DataTypePicker);
})();
})();
@@ -79,13 +79,13 @@
</umb-tab-content>
</div>
<!-- FILTER RESULTS -->
<div ng-if="vm.showFilterResult">
<div ng-if="vm.filterResult">
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_reuse"></localize></h5>
<div ng-repeat="(key,value) in vm.userConfigured">
<div ng-if="(value | filter:vm.searchTerm).length > 0">
<h5>{{key}}</h5>
<div ng-repeat="result in vm.filterResult.userConfigured">
<div ng-if="result.dataTypes.length > 0">
<h5>{{result.group}}</h5>
<ul class="umb-card-grid" ng-mouseleave="vm.hideDetailsOverlay()">
<li ng-repeat="dataType in value | orderBy:'name' | filter: vm.searchTerm"
<li ng-repeat="dataType in result.dataTypes | orderBy:'name'"
ng-mouseover="vm.showDetailsOverlay(dataType)"
ng-click="vm.pickDataType(dataType)"
class="-four-in-row">
@@ -101,11 +101,11 @@
</div>
</div>
<h5 class="-border-bottom -black"><localize key="contentTypeEditor_availableEditors"></localize></h5>
<div ng-repeat="(key,value) in vm.typesAndEditors">
<div ng-if="(value | filter:vm.searchTerm).length > 0">
<h5>{{key}}</h5>
<div ng-repeat="result in vm.filterResult.typesAndEditors">
<div ng-if="result.dataTypes.length > 0">
<h5>{{result.group}}</h5>
<ul class="umb-card-grid" ng-mouseleave="vm.hideDetailsOverlay()">
<li ng-repeat="systemDataType in value | orderBy:'name' | filter: vm.searchTerm"
<li ng-repeat="systemDataType in result.dataTypes | orderBy:'name'"
ng-mouseover="vm.showDetailsOverlay(systemDataType)"
ng-click="vm.pickEditor(systemDataType)"
class="-four-in-row">
@@ -10,7 +10,8 @@
(function () {
"use strict";
function DataTypeSettingsController($scope, dataTypeResource, dataTypeHelper, localizationService, notificationsService, overlayService, formHelper) {
function DataTypeSettingsController($scope, dataTypeResource, dataTypeHelper,
localizationService, notificationsService, overlayService, formHelper, eventsService) {
var vm = this;
@@ -103,27 +104,33 @@
var preValues = dataTypeHelper.createPreValueProps(vm.dataType.preValues);
dataTypeResource.save(vm.dataType, preValues, $scope.model.create).then(function(newDataType) {
$scope.model.dataType = newDataType;
vm.saveButtonState = "success";
dataTypeResource.save(vm.dataType, preValues, $scope.model.create).then(
function(newDataType) {
$scope.model.dataType = newDataType;
var args = { dataType: newDataType };
eventsService.emit("editors.dataTypeSettings.saved", args);
vm.saveButtonState = "success";
if ($scope.model && $scope.model.submit) {
$scope.model.submit($scope.model);
}
}, function(err) {
vm.saveButtonState = "error";
if(err.status === 400) {
if (err.data && (err.data.ModelState)) {
formHelper.handleServerValidation(err.data.ModelState);
if ($scope.model && $scope.model.submit) {
$scope.model.submit($scope.model);
}
}, function(err) {
vm.saveButtonState = "error";
if(err.status === 400) {
if (err.data && (err.data.ModelState)) {
formHelper.handleServerValidation(err.data.ModelState);
for (var e in err.data.ModelState) {
notificationsService.error("Validation", err.data.ModelState[e][0]);
for (var e in err.data.ModelState) {
notificationsService.error("Validation", err.data.ModelState[e][0]);
}
}
}
}
});
);
}
@@ -6,6 +6,6 @@
<p>
<strong>Name:</strong><br/>
<input ng-model="model.name" type="text" name="queryName" ng-change="model.disableSubmitButton = model.name.length === 0"/>
<input style="width: 100%" ng-model="model.name" type="text" name="queryName" ng-change="model.disableSubmitButton = model.name.length === 0"/>
</p>
</div>
</div>
@@ -38,7 +38,7 @@
<span>{{vm.currentVariant.language.name}}</span>
</span>
<umb-dropdown ng-if="vm.dropdownOpen" style="width: 100%; max-height: 250px; overflow-y: scroll; margin-top: 5px;" on-close="vm.dropdownOpen = false" umb-keyboard-list>
<umb-dropdown ng-if="vm.dropdownOpen" style="min-width: 100%; max-height: 250px; overflow-y: auto; margin-top: 5px;" on-close="vm.dropdownOpen = false" umb-keyboard-list>
<umb-dropdown-item class="umb-variant-switcher__item" ng-class="{'umb-variant-switcher_item--current': variant.active, 'umb-variant-switcher_item--not-allowed': variantIsOpen(variant.language.culture)}" ng-repeat="variant in content.variants">
<a href="" class="umb-variant-switcher__name-wrapper" ng-click="selectVariant($event, variant)" prevent-default>
<span class="umb-variant-switcher__name" ng-class="{'bold': variant.language.isDefault}">{{variant.language.name}}</span>
@@ -1,3 +1,3 @@
<div class="umb-rte">
<div id="{{uniqueId}}"></div>
<div class="umb-rte"
id="{{textAreaHtmlId}}">
</div>
@@ -10,10 +10,13 @@
<div class="umb-content-grid__content">
<div class="umb-content-grid__item-name" ng-click="clickItemName(item, $event, $index)" ng-class="{'-light': !item.published && item.updater != null}">
<a class="umb-content-grid__item-name"
ng-href="{{'#' + item.editPath}}"
ng-click="clickItemName(item, $event, $index)"
ng-class="{'-light': !item.published && item.updater != null}">
<i class="umb-content-grid__icon {{ item.icon }}"></i>
<span>{{ item.name }}</span>
</div>
</a>
<ul class="umb-content-grid__details-list" ng-class="{'-light': !item.published && item.updater != null}">
<li class="umb-content-grid__details-item" ng-if="item.state">
@@ -63,7 +63,7 @@
<div class="umb-table-row"
ng-repeat="child in miniListView.children"
ng-click="selectNode(child)"
ng-class="{'umb-table-row--selected':child.selected, 'not-allowed':!child.allowed}">
ng-class="{'-selected':child.selected, 'not-allowed':!child.allowed}">
<div class="umb-table-cell umb-table-cell--auto-width" ng-class="{'umb-table-cell--faded':child.published === false}">
<div class="flex items-center">
<ins class="icon-navigation-right umb-table__row-expand" ng-click="openNode($event, child)" ng-class="{'umb-table__row-expand--hidden': child.metaData.hasChildren !== true}">&nbsp;</ins>
@@ -10,7 +10,6 @@
ng-checked="vm.isSelectedAll()">
</div>
<div class="umb-table-cell umb-table__name">
<a class="umb-table-head__link sortable" href="#" ng-click="vm.sort('Name', true, true)" prevent-default>
<localize key="general_name">Name</localize>
<i class="umb-table-head__icon icon" ng-class="{'icon-navigation-up': vm.isSortDirection('Name', 'asc'), 'icon-navigation-down': vm.isSortDirection('Name', 'desc')}"></i>
</a>
@@ -30,9 +29,9 @@
</div>
<!-- Listview body section -->
<div class="umb-table-body">
<div class="umb-table-row"
<div class="umb-table-row -selectable"
ng-repeat="item in vm.items track by $index"
ng-class="{'umb-table-row--selected':item.selected}"
ng-class="{'-selected':item.selected, '-light':!item.published && item.updater != null}"
ng-click="vm.selectItem(item, $index, $event)">
<div class="umb-table-cell">
@@ -40,7 +39,8 @@
<i class="umb-table-body__icon umb-table-body__checkicon icon-check"></i>
</div>
<div class="umb-table-cell umb-table__name">
<a title="{{ item.name }}" class="umb-table-body__link" href=""
<a title="{{ item.name }}" class="umb-table-body__link"
ng-href="{{'#' + item.editPath}}"
ng-click="vm.clickItem(item, $event)"
ng-bind="item.name">
</a>
@@ -5,7 +5,7 @@
<form name="vm.domainForm" ng-submit="vm.save()" novalidate>
<div ng-hide="vm.loading" class="umb-dialog-body">
<umb-pane ng-if="!currentNode.metaData.variesByCulture">
<h5 class="umb-pane-title"><localize key="assignDomain_setLanguage">Culture</localize></h5>
<label for="assignDomain_language" class="control-label"><localize key="general_language"></localize></label>
@@ -18,14 +18,14 @@
<h5 class="umb-pane-title"><localize key="assignDomain_setDomains">Domains</localize></h5>
<small class="db" style="margin-bottom: 10px;">
<localize key="assignDomain_domainHelp"></localize>
<localize key="assignDomain_domainHelpWithVariants"></localize>
</small>
<div class="umb-el-wrap hidelabel">
<table class="table table-condensed table-bordered domains" style="margin-bottom: 10px;" ng-if="vm.domains.length > 0">
<thead>
<tr>
<th>
<localize key="assignDomain_domain"></localize>
<localize key="assignDomain_domain"></localize>
<span class="umb-control-required">*</span>
</th>
<th>
@@ -45,17 +45,17 @@
<span ng-show="domain.duplicate" class="help-inline"><localize key="assignDomain_duplicateDomain"></localize><span ng-show="domain.other">({{domain.other}})</span></span>
</td>
<td>
<select
style="width: 100%; margin-bottom: 0;"
name="domain_language_{{$index}}"
class="language"
ng-model="domain.lang"
<select
style="width: 100%; margin-bottom: 0;"
name="domain_language_{{$index}}"
class="language"
ng-model="domain.lang"
ng-options="lang.name for lang in vm.languages"
required>
</select>
</td>
<td>
<umb-button
<umb-button
icon="icon-trash"
action="vm.removeDomain($index)"
type="button"
@@ -66,8 +66,8 @@
</tbody>
</table>
</div>
<umb-button
<umb-button
label-key="assignDomain_addNew"
action="vm.addDomain()"
type="button"
@@ -80,14 +80,14 @@
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<umb-button
<umb-button
label-key="general_close"
action="vm.closeDialog()"
type="button"
button-style="link">
</umb-button>
<umb-button
<umb-button
label-key="buttons_save"
type="submit"
button-style="success"
@@ -97,5 +97,5 @@
</div>
</form>
</div>
@@ -46,8 +46,7 @@
if (data.language !== "undefined") {
var lang = vm.languages.filter(function (l) {
return matchLanguageById(l, data.language.Id);
return matchLanguageById(l, data.language);
});
if (lang.length > 0) {
vm.language = lang[0];
@@ -116,6 +115,7 @@
if(response.valid) {
vm.submitButtonState = "success";
closeDialog();
// show validation messages for each domain
} else {
@@ -27,7 +27,7 @@
{{vm.variants[0].releaseDateFormatted}}
</button>
<a ng-hide="vm.variants[0].releaseDate" href="" class="bold" style="color: #00aea2; text-decoration: underline;">
<a ng-hide="vm.variants[0].releaseDate" href="">
<localize key="content_setDate">Set date</localize>
</a>
</div>
@@ -59,7 +59,7 @@
{{vm.variants[0].expireDateFormatted}}
</button>
<a ng-hide="vm.variants[0].expireDate" href="" class="bold" style="color: #00aea2; text-decoration: underline;">
<a ng-hide="vm.variants[0].expireDate" href="">
<localize key="content_setDate">Set date</localize>
</a>
</div>
@@ -84,8 +84,8 @@
<div class="umb-list umb-list--condensed">
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.dirtyVariantFilter">
<ng-form name="scheduleSelectorForm">
<div class="umb-list-item" ng-repeat="variant in vm.variants">
<ng-form name="scheduleSelectorForm" style="width:100%;">
<div class="flex">
<umb-checkbox
@@ -106,9 +106,9 @@
</span>
</label>
<div class="flex items-center" style="margin-top: 10px; margin-bottom: 10px;">
<div class="date-wrapper-mini">
<div class="date-wrapper-mini__date" ng-if="vm.dirtyVariantFilter(variant) && (variant.releaseDate || variant.save)">
<div class="flex items-center" ng-if="variant.releaseDate || variant.save">
<div style="font-size: 13px; margin-right: 5px;">Publish:<em ng-show="!variant.save">&nbsp;&nbsp;{{variant.releaseDateFormatted}}</em></div>
<div class="btn-group flex" style="font-size: 14px; margin-right: 10px;" ng-if="variant.save">
@@ -123,7 +123,7 @@
{{variant.releaseDateFormatted}}
</button>
<a ng-hide="variant.releaseDate" href="" class="bold" style="color: #00aea2; text-decoration: underline;">
<a ng-hide="variant.releaseDate" href="">
<localize key="content_setDate">Set date</localize>
</a>
</div>
@@ -134,7 +134,7 @@
</div>
</div>
<div class="flex items-center" ng-if="variant.expireDate || variant.save">
<div class="date-wrapper-mini__date" ng-if="variant.expireDate || variant.save">
<div style="font-size: 13px; margin-right: 5px;">Unpublish:<em ng-show="!variant.save">&nbsp;&nbsp;{{variant.expireDateFormatted}}</em></div>
<div class="btn-group flex" style="font-size: 14px;" ng-if="variant.save">
@@ -149,7 +149,7 @@
{{variant.expireDateFormatted}}
</button>
<a ng-hide="variant.expireDate" href="" class="bold" style="color: #00aea2; text-decoration: underline;">
<a ng-hide="variant.expireDate" href="">
<localize key="content_setDate">Set date</localize>
</a>
</div>
@@ -182,24 +182,6 @@
<br />
</div>
<div class="umb-list umb-list--condensed" ng-if="vm.hasPristineVariants">
<div style="margin-bottom: 15px; font-weight: bold;">
<p><localize key="content_publishedLanguages"></localize></p>
</div>
<div class="umb-list-item" ng-repeat="variant in vm.variants | filter:vm.pristineVariantFilter track by variant.language.culture">
<div>
<div style="margin-bottom: 2px;">
<span>{{ variant.language.name }}</span>
</div>
<div class="umb-permission__description">
<umb-variant-state variant="variant"></umb-variant-state>
<span ng-show="variant.language.isMandatory"> - <localize key="languages_mandatoryLanguage"></localize></span>
</div>
</div>
</div>
</div>
</div>
</div>
@@ -13,7 +13,8 @@
type="button"
size="s"
action="vm.disableUrlTracker($event)"
label-key="redirectUrls_disableUrlTracker">
label-key="redirectUrls_disableUrlTracker"
button-style="white">
</umb-button>
<umb-button
@@ -22,7 +23,8 @@
size="s"
button-style="success"
action="vm.enableUrlTracker()"
label-key="redirectUrls_enableUrlTracker">
label-key="redirectUrls_enableUrlTracker"
button-style="success">
</umb-button>
</umb-editor-sub-header-section>
@@ -22,7 +22,7 @@ function startUpVideosDashboardController($scope, dashboardResource) {
angular.module("umbraco").controller("Umbraco.Dashboard.StartupVideosController", startUpVideosDashboardController);
function startUpDynamicContentController($timeout, $scope, dashboardResource, assetsService, tourService, eventsService) {
function startUpDynamicContentController($q, $timeout, $scope, dashboardResource, assetsService, tourService, eventsService) {
var vm = this;
var evts = [];
@@ -1,4 +1,4 @@
<div data-element="editor-logs" ng-controller="Umbraco.Editors.LogViewer.OverviewController as vm" class="clearfix">
<div data-element="editor-logs" ng-controller="Umbraco.Editors.LogViewer.OverviewController as vm" class="clearfix" id="logview">
<umb-editor-view footer="false">
@@ -24,8 +24,8 @@
</umb-box>
</div>
<div class="umb-package-details" ng-show="!vm.loading && vm.canLoadLogs">
<div class="umb-package-details__main-content">
<div class="umb-logviewer" ng-show="!vm.loading && vm.canLoadLogs">
<div class="umb-logviewer__main-content">
<!-- Saved Searches -->
<umb-box>
<umb-box-header title="Saved Searches"></umb-box-header>
@@ -68,7 +68,7 @@
</umb-box>
</div>
<div class="umb-package-details__sidebar">
<div class="umb-logviewer__sidebar">
<!-- No of Errors -->
<umb-box ng-click="vm.searchLogQuery('Has(@Exception)')" style="cursor:pointer;">
<umb-box-header title="Number of Errors"></umb-box-header>
@@ -95,4 +95,4 @@
</div>
</umb-editor-container>
</umb-editor-view>
</div>
</div>
@@ -51,18 +51,19 @@
<div style="position:relative; width:100%;">
<!-- Search/expression filter -->
<input class="form-control search-input" type="text" ng-model="vm.logOptions.filterExpression" style="width:100%;" placeholder="Search logs&hellip;" />
<input class="form-control search-input" type="text" ng-model="vm.logOptions.filterExpression" style="width:100%; padding-right: 160px;" placeholder="Search logs&hellip;" />
<!-- Save Search & Clear Search icon buttons -->
<ins class="icon-rate" ng-if="vm.checkForSavedSearch()" ng-click="vm.addToSavedSearches()" style="float: left; position: absolute; top: 0; line-height: 32px; right: 160px; color: #fdb45c; cursor: pointer;">&nbsp;</ins>
<ins class="icon-wrong" ng-if="vm.logOptions.filterExpression" ng-click="vm.resetSearch()" style="float: left; position: absolute; top: 0; line-height: 32px; right: 140px; color: #bbbabf; cursor: pointer;">&nbsp;</ins>
<ins class="icon-rate" ng-show="vm.checkForSavedSearch()" ng-click="vm.addToSavedSearches()" style="position: absolute; top: 0; line-height: 32px; right: 140px; color: #fdb45c; cursor: pointer;">&nbsp;</ins>
<ins class="icon-wrong" ng-show="vm.logOptions.filterExpression" ng-click="vm.resetSearch()" style="position: absolute; top: 0; line-height: 32px; right: 120px; color: #bbbabf; cursor: pointer;">&nbsp;</ins>
<a class="umb-variant-switcher__toggle ng-scope" href="" ng-click="vm.dropdownOpen = !vm.dropdownOpen" style="top:0;">
<span class="ng-binding">Example Searches</span>
<ins class="umb-variant-switcher__expand icon-navigation-down" ng-class="{'icon-navigation-down': !vm.dropdownOpen, 'icon-navigation-up': vm.dropdownOpen}">&nbsp;</ins>
<!-- Saved Searches -->
<a class="umb-variant-switcher__toggle ng-scope" href="" ng-click="vm.dropdownOpen = !vm.dropdownOpen" style="top: 1px; right: 0; position: absolute;">
<span class="ng-binding">Saved Searches</span>
<ins class="umb-variant-switcher__expand icon-navigation-down" ng-class="{'icon-navigation-down': !vm.dropdownOpen, 'icon-navigation-up': vm.dropdownOpen}" style="margin-top: 0;"></ins>
</a>
<!-- Common Searches Dropdown -->
<!-- Saved Searches Dropdown -->
<umb-dropdown ng-if="vm.dropdownOpen" style="width: 100%; max-height: 250px; overflow-y: scroll; margin-top: -10px;" on-close="vm.dropdownOpen = false" umb-keyboard-list>
<umb-dropdown-item class="umb-variant-switcher__item" ng-class="{'umb-variant-switcher_item--current': variant.active}" ng-repeat="search in vm.searches">
<a href="" class="umb-variant-switcher__name-wrapper" ng-click="vm.selectSearch(search)" prevent-default>
@@ -33,9 +33,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.CheckboxListContro
}
function updateViewModel(configItems) {
//check if it's already in sync
//get the checked vals from the view model
var selectedVals = _.map(
_.filter($scope.selectedItems,
@@ -47,22 +45,22 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.CheckboxListContro
return m.value;
}
);
//get all of the same values between the arrays
var same = _.intersection($scope.model.value, selectedVals);
//if the lengths are the same as the value, then we are in sync, just exit
if (same.length === $scope.model.value.length === selectedVals.length) {
//if the length is zero, then we are in sync, just exit.
if (_.difference($scope.model.value, selectedVals).length === 0) {
return;
}
$scope.selectedItems = [];
var iConfigItem;
for (var i = 0; i < configItems.length; i++) {
var isChecked = _.contains($scope.model.value, configItems[i].value);
iConfigItem = configItems[i];
var isChecked = _.contains($scope.model.value, iConfigItem.value);
$scope.selectedItems.push({
checked: isChecked,
key: configItems[i].id,
val: configItems[i].value
key: iConfigItem.id,
val: iConfigItem.value
});
}
}
@@ -1,25 +1,26 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.Grid.EmbedController",
function ($scope, $timeout, $sce, editorService) {
function onInit() {
$scope.trustedValue = null;
$scope.trustedValue = $sce.trustAsHtml($scope.control.value);
if(!$scope.control.value) {
$timeout(function(){
if($scope.control.$initializing){
$scope.setEmbed();
}
}, 200);
}
function getEmbed() {
return $sce.trustAsHtml($scope.control.value);
}
$scope.setEmbed = function(){
$scope.embedHtml = getEmbed();
$scope.$watch('control.value', function(newValue, oldValue) {
if(angular.equals(newValue, oldValue)){
return; // simply skip that
}
$scope.embedHtml = getEmbed();
}, false);
$scope.setEmbed = function() {
var embed = {
submit: function(model) {
$scope.control.value = model.embed.preview;
$scope.trustedValue = $sce.trustAsHtml(model.embed.preview);
editorService.close();
},
close: function() {
@@ -28,6 +29,5 @@ angular.module("umbraco")
};
editorService.embed(embed);
};
onInit();
});
@@ -1,10 +1,10 @@
<div ng-controller="Umbraco.PropertyEditors.Grid.EmbedController">
<div class="umb-editor-placeholder" ng-click="setEmbed()" ng-if="trustedValue === null">
<div class="umb-editor-placeholder" ng-click="setEmbed()" ng-if="control.value === null">
<i class="icon icon-movie-alt"></i>
<div class="help-text"><localize key="grid_clickToEmbed">Click to embed</localize></div>
</div>
<div ng-if="control.value" ng-bind-html="trustedValue"></div>
<div ng-if="control.value" ng-bind-html="embedHtml"></div>
</div>
@@ -1,20 +1,18 @@
angular.module("umbraco")
.controller("Umbraco.PropertyEditors.Grid.MediaController",
function ($scope, $timeout, userService, editorService) {
$scope.thumbnailUrl = getThumbnailUrl();
if (!$scope.model.config.startNodeId) {
userService.getCurrentUser().then(function (userData) {
$scope.model.config.startNodeId = userData.startMediaIds.length !== 1 ? -1 : userData.startMediaIds[0];
$scope.model.config.startNodeIsVirtual = userData.startMediaIds.length !== 1;
});
}
function onInit() {
if($scope.control.value){
$scope.setUrl();
}
}
$scope.setImage = function(){
var startNodeId = $scope.model.config && $scope.model.config.startNodeId ? $scope.model.config.startNodeId : undefined;
var startNodeIsVirtual = startNodeId ? $scope.model.config.startNodeIsVirtual : undefined;
@@ -34,10 +32,8 @@ angular.module("umbraco")
id: selectedImage.id,
udi: selectedImage.udi,
image: selectedImage.image,
altText: selectedImage.altText
};
$scope.setUrl();
caption: selectedImage.altText
};
editorService.close();
},
@@ -48,10 +44,18 @@ angular.module("umbraco")
editorService.mediaPicker(mediaPicker);
};
$scope.$watch('control.value', function(newValue, oldValue) {
if(angular.equals(newValue, oldValue)){
return; // simply skip that
}
$scope.thumbnailUrl = getThumbnailUrl();
}, true);
function getThumbnailUrl() {
$scope.setUrl = function(){
if($scope.control.value.image){
if($scope.control.value && $scope.control.value.image) {
var url = $scope.control.value.image;
if($scope.control.editor.config && $scope.control.editor.config.size){
@@ -70,10 +74,10 @@ angular.module("umbraco")
{
url += "?width=800&upscale=false&animationprocessmode=false"
}
$scope.url = url;
return url;
}
return null;
};
onInit();
});
@@ -5,11 +5,10 @@
<div ng-id="!control.$inserted" class="help-text"><localize key="grid_clickToInsertImage">Click to insert image</localize></div>
</div>
<div ng-if="control.value">
<div ng-if="thumbnailUrl !== null">
<img
ng-if="url"
ng-click="setImage()"
ng-src="{{url}}"
ng-src="{{thumbnailUrl}}"
class="fullSizeImage" />
<input type="text" class="caption" ng-model="control.value.caption" localize="placeholder" placeholder="@grid_placeholderImageCaption" />
</div>
File diff suppressed because it is too large Load Diff

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