Compare commits

..

1 Commits

Author SHA1 Message Date
Rasmus John Pedersen 87716c7ed8 Only add "Move" action once 2018-10-11 19:46:43 +02:00
213 changed files with 7051 additions and 9256 deletions
+1 -2
View File
@@ -13,5 +13,4 @@ indent_size = 4
# Trim trailing whitespace, limited support. # Trim trailing whitespace, limited support.
# https://github.com/editorconfig/editorconfig/wiki/Property-research:-Trim-trailing-spaces # https://github.com/editorconfig/editorconfig/wiki/Property-research:-Trim-trailing-spaces
trim_trailing_whitespace = true trim_trailing_whitespace = true
csharp_prefer_braces = false : none
+6 -4
View File
@@ -1,4 +1,4 @@
_Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/temp8/.github/V8_GETTING_STARTED.md) to go to the v8 branch_ _Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-CMS/blob/temp8/docs/CONTRIBUTING.md) to go to the v8 branch_
# Contributing to Umbraco CMS # Contributing to Umbraco CMS
👍🎉 First off, thanks for taking the time to contribute! 🎉👍 👍🎉 First off, thanks for taking the time to contribute! 🎉👍
@@ -16,9 +16,9 @@ This document gives you a quick overview on how to get started, we will link to
## Guidelines for contributions we welcome ## 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 valueable time. Not all changes are wanted so on occassion we might close a PR without merging it. We will give you feedback why we can't accept your changes and we'll be nice about it, thanking you for spending your valueable time.
We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md). Make sure to talk to us before making large changes. We have [documented what we consider small and large changes](CONTRIBUTION_GUIDELINES.md), make sure to talk to us before making large changes.
Remember, if an issue is in the `Up for grabs` list or you've asked for some feedback before you sent us a PR, your PR will not be closed as unwanted. Remember, if an issue is in the `Up for grabs` list or you've asked for some feedback before you sent us a PR, your PR will not be closed as unwanted.
@@ -36,7 +36,7 @@ Great question! The short version goes like this:
* **Build** - build your fork of Umbraco locally as described in [building Umbraco from source code](BUILD.md) * **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) * **Change** - make your changes, experiment, have fun, explore and learn, and don't be afraid. We welcome all contributions and will [happily give feedback](#questions)
* **Commit** - done? Yay! 🎉 It is recommended to create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `12345` * **Commit** - done? Yay! 🎉 It is recommended to create a new branch now and name it after the issue you're fixing, we usually follow the format: `temp-U4-12345`. This means it's a temporary branch for the particular issue you're working on, in this case `U4-12345`
* **Push** - great, now you can push the changes up to your fork on GitHub * **Push** - great, now you can push the changes up to your fork on GitHub
* **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go. * **Create pull request** - exciting! You're ready to show us your changes (or not quite ready, you just need some feedback to progress). GitHub has picked up on the new branch you've pushed and will offer to create a Pull Request. Click that green button and away you go.
@@ -72,6 +72,7 @@ The pull request team consists of a member of Umbraco HQ, [Sebastiaan](https://g
- [Anders Bjerner](https://github.com/abjerner) - [Anders Bjerner](https://github.com/abjerner)
- [Dave Woestenborghs](https://github.com/dawoe) - [Dave Woestenborghs](https://github.com/dawoe)
- [Emma Burstow](https://github.com/emmaburstow) - [Emma Burstow](https://github.com/emmaburstow)
- [Kyle Weems](https://github.com/cssquirrel)
- [Poornima Nayar](https://github.com/poornimanayar) - [Poornima Nayar](https://github.com/poornimanayar)
These wonderful volunteers will provide you with a first reply to your PR, review and test out your changes and might ask more questions. After that they'll let Umbraco HQ know if everything seems okay. 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.
@@ -82,6 +83,7 @@ You can get in touch with [the PR team](#the-pr-team) in multiple ways, we love
- 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 - If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.com/forum/contributing-to-umbraco-cms/) forum, the team monitors that one closely - Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.com/forum/contributing-to-umbraco-cms/) forum, the team monitors that one closely
- We're also [active in the Gitter chatroom](https://gitter.im/umbraco/Umbraco-CMS)
## Code of Conduct ## Code of Conduct
+8 -12
View File
@@ -19,13 +19,13 @@ When contributing code to Umbraco there's plenty of things you'll want to know,
* [What branch should I target for my contributions?](#what-branch-should-i-target-for-my-contributions) * [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) * [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) * [Keeping your Umbraco fork in sync with the main repository](#keeping-your-umbraco-fork-in-sync-with-the-main-repository)
## How Can I Contribute? ## How Can I Contribute?
### Reporting Bugs ### 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 🔎. This section guides you through submitting a bug report for Umbraco CMS. Following these guidelines helps maintainers and the community understand your report 📝, reproduce the behavior 💻 💻, and find related reports 🔎.
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](https://github.com/umbraco/Umbraco-CMS/issues/new/choose), the information it asks for helps us resolve issues faster. Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report). Fill out [the required template](http://issues.umbraco.org/issues#newissue=61-30118), the information it asks for helps us resolve issues faster.
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one. > **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.
@@ -52,7 +52,7 @@ Provide more context by answering these questions:
Include details about your configuration and environment: Include details about your configuration and environment:
* **Which version of Umbraco are you using?** * **Which version of Umbraco are you using?**
* **What is the environment you're using Umbraco in?** Is this a problem on your local machine or on a server. Tell us about your configuration: Windows version, IIS/IISExpress, database type, etc. * **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?** * **Which packages do you have installed?**
@@ -69,7 +69,9 @@ Some additional hints that may be helpful:
### Your First Code Contribution ### Your First Code Contribution
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` and issues](https://issues.umbraco.org/issues?q=&project=U4&tagValue=upforgrabs&release=&issueType=&search=search) or on the [new issue tracker](https://github.com/umbraco/Umbraco-CMS/issues?q=is%3Aopen+is%3Aissue+label%3Acommunity%2Fup-for-grabs). Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` and issues](http://issues.umbraco.org/issues/U4?q=%28project%3A+%7BU4%7D+Difficulty%3A+%7BVery+Easy%7D+%23Easy+%23Unresolved+Priority%3A+Normal+%23Major+%23Show-stopper+State%3A+-%7BIn+Progress%7D+sort+by%3A+votes+Affected+versions%3A+-6.*+Affected+versions%3A+-4.*%29+OR+%28tag%3A+%7BUp+For+Grabs%7D+%23Unresolved+%29).
The issue list is sorted by total number of upvotes. While not perfect, number of upvotes is a reasonable proxy for impact a given change will have.
### Pull Requests ### Pull Requests
@@ -80,7 +82,7 @@ The most successful pull requests usually look a like this:
* Unit tests, while optional are awesome, thank you! * Unit tests, while optional are awesome, thank you!
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.com/documentation/Reference/) is generated * New code is commented with documentation from which [the reference documentation](https://our.umbraco.com/documentation/Reference/) is generated
Again, these are guidelines, not strict requirements. Again, these are guidelines, not strict requirements.
## Making changes after the PR was opened ## Making changes after the PR was opened
@@ -90,7 +92,7 @@ If you make the corrections we ask for in the same branch and push them to your
To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up. To be honest, we don't like rules very much. We trust you have the best of intentions and we encourage you to create working code. If it doesn't look perfect then we'll happily help clean it up.
That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc. That said, the Umbraco development team likes to follow the hints that ReSharper gives us (no problem if you don't have this installed) and we've added a `.editorconfig` file so that Visual Studio knows what to do with whitespace, line endings, etc.
## What should I know before I get started? ## What should I know before I get started?
@@ -125,12 +127,6 @@ We like to use [Gitflow as much as possible](https://jeffkreeftmeijer.com/git-fl
### Building Umbraco from source code ### 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. The easiest way to get started is to run `build.bat` which will build both the backoffice (also known as "Belle") and the Umbraco core. You can then easily start debugging from Visual Studio, or if you need to debug Belle you can run `gulp dev` in `src\Umbraco.Web.UI.Client`. See [this page](BUILD.md) for more details.
Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 (version 15.3 or higher, [the community edition is free](https://www.visualstudio.com/thank-you-downloading-visual-studio/?sku=Community&rel=15) for you to use to contribute to Open Source projects). In Visual Studio, find the Task Runner Explorer (in the View menu under Other Windows) and run the build task under the gulpfile. Alternatively, you can open `src\umbraco.sln` in Visual Studio 2017 (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.
+3 -4
View File
@@ -1,12 +1,11 @@
### Prerequisites ### Prerequisites
- [ ] I have [created an issue](https://github.com/umbraco/Umbraco-CMS/issues) for the proposed changes in this PR, the link is:
- [ ] I have added steps to test this contribution in the description below - [ ] I have added steps to test this contribution in the description below
If there's an existing issue for this PR then this fixes: <!-- link to the issue here! -->
### Description ### 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 -->
<!-- Make sure to link to the related issue number so we can easily find it in the issue tracker -->
<!-- Thanks for contributing to Umbraco CMS! --> <!-- Thanks for contributing to Umbraco CMS! -->
+11 -11
View File
@@ -4,7 +4,7 @@ _Looking for Umbraco version 8? [Click here](https://github.com/umbraco/Umbraco-
Umbraco CMS 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) The friendliest, most flexible and fastest growing ASP.NET CMS used by more than 443,000 websites worldwide: [https://umbraco.com](https://umbraco.com)
[![ScreenShot](img/vimeo.png)](https://vimeo.com/172382998/) [![ScreenShot](img/vimeo.png)](https://vimeo.com/172382998/)
@@ -17,34 +17,34 @@ Umbraco is a free open source Content Management System built on the ASP.NET pla
## Umbraco - The Friendly CMS ## 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. For the first time on the Microsoft platform, there is a free user and developer friendly CMS that makes it quick and easy to create websites - or a breeze to build complex web applications. Umbraco has award-winning integration capabilities and supports ASP.NET MVC or Web Forms, including User and Custom Controls, out of the box.
Umbraco is not only loved by developers, but is a content editor's dream. Enjoy intuitive editing tools, media management, responsive views, and approval workflows to send your content live. Umbraco is not only loved by developers, but is a content editors dream. Enjoy intuitive editing tools, media management, responsive views and approval workflows to send your content live.
Used by more than 443,000 active websites including Carlsberg, Segway, Amazon and Heinz and **The Official ASP.NET and IIS.NET website from Microsoft** ([https://asp.net](https://asp.net) / [https://iis.net](https://iis.net)), you can be sure that the technology is proven, stable and 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. Used by more than 443,000 active websites including Carlsberg, Segway, Amazon and Heinz and **The Official ASP.NET and IIS.NET website from Microsoft** ([https://asp.net](https://asp.net) / [https://iis.net](https://iis.net)), you can be sure that the technology is proven, stable and scales. Backed by the team at Umbraco HQ, and supported by a dedicated community of over 220,000 craftspeople globally, you can trust that Umbraco is a safe choice and is here to stay.
To view more examples, please visit [https://umbraco.com/case-studies-testimonials/](https://umbraco.com/case-studies-testimonials/) To view more examples, please visit [https://umbraco.com/case-studies-testimonials/](https://umbraco.com/case-studies-testimonials/)
## Why Open Source? ## 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. 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 ## Trying out Umbraco CMS
[Umbraco Cloud](https://umbraco.com/cloud) is the easiest and fastest way to use Umbraco yet, with full support for all your custom .NET code and integrations. You're up and running in less than a minute, and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14-day trial, no credit card needed. [Umbraco Cloud](https://umbraco.com/cloud) is the easiest and fastest way to use Umbraco yet with full support for all your custom .NET code and intergrations. You're up and running in less than a minute and your life will be made easier with automated upgrades and a built-in deployment engine. We offer a free 14 day trial, no credit card needed.
If you want to DIY, you can [download Umbraco](https://our.umbraco.com/download) either as a ZIP file or via NuGet. It's the same version of Umbraco CMS that powers Umbraco Cloud, but you'll need to find a place to host 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 yourself and handling deployments and upgrades is all down to you.
## Community ## Community
Our friendly community is available 24/7 at the community hub we call ["Our Umbraco"](https://our.umbraco.com). Our Umbraco features forums for questions and answers, documentation, downloadable plugins for Umbraco, and a rich collection of community resources. Our friendly community is available 24/7 at the community hub we call ["Our Umbraco"](https://our.umbraco.com). Our Umbraco feature forums for questions and answers, documentation, downloadable plugins for Umbraco and a rich collection of community resources.
## Contribute to Umbraco ## Contribute to Umbraco
Umbraco is contribution-focused and community-driven. If you want to contribute back to Umbraco, please check out our [guide to contributing](CONTRIBUTING.md). Umbraco is contribution focused and community driven. If you want to contribute back to Umbraco please check out our [guide to contributing](CONTRIBUTING.md).
## Found a bug? ## Found a bug?
Another way you can contribute to Umbraco is by providing issue reports. For information on how to submit an issue report, refer to our [online guide for reporting issues](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md). 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](https://github.com/umbraco/Umbraco-CMS/blob/dev-v7/.github/CONTRIBUTING.md).
You can comment and report issues on the [github issue tracker](https://github.com/umbraco/Umbraco-CMS/issues). 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). 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).
-1
View File
@@ -90,7 +90,6 @@ src/Umbraco.Web.UI.Client/[Bb]uild/*
src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/ src/Umbraco.Web.UI.Client/[Bb]uild/[Bb]elle/
src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/ src/Umbraco.Web.UI/[Uu]ser[Cc]ontrols/
src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css src/Umbraco.Web.UI.Client/src/[Ll]ess/*.css
src/Umbraco.Web.UI.Client/vwd.webinfo
tools/NDepend/ tools/NDepend/
src/Umbraco.Web.UI/App_Plugins/* src/Umbraco.Web.UI/App_Plugins/*
@@ -7,7 +7,7 @@
<span class="pull-right"> <span class="pull-right">
<a href="#top">Back to top</a> <a href="#top">Back to top</a>
</span> </span>
<span>Copyright © 2016-present Umbraco<br>Generated by <strong>DocFX</strong></span> <span>Copyright © 2016 Umbraco<br>Generated by <strong>DocFX</strong></span>
</div> </div>
</div> </div>
</footer> </footer>
+1 -1
View File
@@ -9,6 +9,6 @@ IF ERRORLEVEL 1 (
:error :error
ECHO. ECHO.
ECHO Cannot run build\build.ps1. ECHO Can not run build\build.ps1.
ECHO If this is due to a SecurityError then please refer to BUILD.md for help! ECHO If this is due to a SecurityError then please refer to BUILD.md for help!
ECHO. ECHO.
@@ -70,8 +70,7 @@ namespace Umbraco.Core.Configuration
var config = WebConfigurationManager.OpenWebConfiguration(appPath); var config = WebConfigurationManager.OpenWebConfiguration(appPath);
var settings = (MailSettingsSectionGroup)config.GetSectionGroup("system.net/mailSettings"); var settings = (MailSettingsSectionGroup)config.GetSectionGroup("system.net/mailSettings");
// note: "noreply@example.com" is/was the sample SMTP from email - we'll regard that as "not configured" if (settings == null || settings.Smtp == null) return false;
if (settings == null || settings.Smtp == null || "noreply@example.com".Equals(settings.Smtp.From, StringComparison.OrdinalIgnoreCase)) return false;
if (settings.Smtp.SpecifiedPickupDirectory != null && string.IsNullOrEmpty(settings.Smtp.SpecifiedPickupDirectory.PickupDirectoryLocation) == false) if (settings.Smtp.SpecifiedPickupDirectory != null && string.IsNullOrEmpty(settings.Smtp.SpecifiedPickupDirectory.PickupDirectoryLocation) == false)
return true; return true;
if (settings.Smtp.Network != null && string.IsNullOrEmpty(settings.Smtp.Network.Host) == false) if (settings.Smtp.Network != null && string.IsNullOrEmpty(settings.Smtp.Network.Host) == false)
+2 -7
View File
@@ -21,12 +21,7 @@ namespace Umbraco.Core.Models
string Description { get; set; } string Description { get; set; }
/// <summary> /// <summary>
/// Gets or sets the icon for the content type. The value is a CSS class name representing /// Gets or Sets the Icon for the ContentType
/// the icon (eg. <c>icon-home</c>) along with an optional CSS class name representing the
/// color (eg. <c>icon-blue</c>). Put together, the value for this scenario would be
/// <c>icon-home color-blue</c>.
///
/// If a class name for the color isn't specified, the icon color will default to black.
/// </summary> /// </summary>
string Icon { get; set; } string Icon { get; set; }
@@ -124,4 +119,4 @@ namespace Umbraco.Core.Models
/// <returns></returns> /// <returns></returns>
bool MovePropertyType(string propertyTypeAlias, string propertyGroupName); bool MovePropertyType(string propertyTypeAlias, string propertyGroupName);
} }
} }
+3 -7
View File
@@ -3,7 +3,6 @@ using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Security.Cryptography;
using Umbraco.Core.Cache; using Umbraco.Core.Cache;
using Umbraco.Core.Configuration; using Umbraco.Core.Configuration;
using Umbraco.Core.IO; using Umbraco.Core.IO;
@@ -36,12 +35,9 @@ namespace Umbraco.Core.Models
/// A list of 5 different sized avatar URLs /// A list of 5 different sized avatar URLs
/// </returns> /// </returns>
internal static string[] GetUserAvatarUrls(this IUser user, ICacheProvider staticCache) internal static string[] GetUserAvatarUrls(this IUser user, ICacheProvider staticCache)
{ {
// If FIPS is required, never check the Gravatar service as it only supports MD5 hashing. //check if the user has explicitly removed all avatars including a gravatar, this will be possible and the value will be "none"
// Unfortunately, if the FIPS setting is enabled on Windows, using MD5 will throw an exception if (user.Avatar == "none")
// and the website will not run.
// Also, check if the user has explicitly removed all avatars including a gravatar, this will be possible and the value will be "none"
if (user.Avatar == "none" || CryptoConfig.AllowOnlyFipsAlgorithms)
{ {
return new string[0]; return new string[0];
} }
@@ -158,7 +158,9 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
Name = indexName, Name = indexName,
IndexType = attribute.IndexType, IndexType = attribute.IndexType,
ColumnName = columnName, ColumnName = columnName,
TableName = tableName, TableName = tableName,
IsClustered = attribute.IndexType == IndexTypes.Clustered,
IsUnique = attribute.IndexType == IndexTypes.UniqueNonClustered
}; };
if (string.IsNullOrEmpty(attribute.ForColumns) == false) if (string.IsNullOrEmpty(attribute.ForColumns) == false)
@@ -172,4 +174,4 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
return definition; return definition;
} }
} }
} }
@@ -1,4 +1,3 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Umbraco.Core.Persistence.DatabaseAnnotations; using Umbraco.Core.Persistence.DatabaseAnnotations;
@@ -15,13 +14,9 @@ namespace Umbraco.Core.Persistence.DatabaseModelDefinitions
public virtual string SchemaName { get; set; } public virtual string SchemaName { get; set; }
public virtual string TableName { get; set; } public virtual string TableName { get; set; }
public virtual string ColumnName { get; set; } public virtual string ColumnName { get; set; }
[Obsolete("Use the IndexType property instead and set it to IndexTypes.UniqueNonClustered")]
public virtual bool IsUnique { get; set; } public virtual bool IsUnique { get; set; }
[Obsolete("Use the IndexType property instead and set it to IndexTypes.Clustered")]
public bool IsClustered { get; set; } public bool IsClustered { get; set; }
public virtual ICollection<IndexColumnDefinition> Columns { get; set; } public virtual ICollection<IndexColumnDefinition> Columns { get; set; }
public IndexTypes IndexType { get; set; } public IndexTypes IndexType { get; set; }
} }
} }
@@ -140,9 +140,6 @@ namespace Umbraco.Core.Persistence.Factories
/// <returns></returns> /// <returns></returns>
internal static bool TryMatch(string text, out string mediaPath) internal static bool TryMatch(string text, out string mediaPath)
{ {
//TODO: In v8 we should allow exposing this via the property editor in a much nicer way so that the property editor
// can tell us directly what any URL is for a given property if it contains an asset
mediaPath = null; mediaPath = null;
if (string.IsNullOrWhiteSpace(text)) if (string.IsNullOrWhiteSpace(text))
@@ -158,4 +155,4 @@ namespace Umbraco.Core.Persistence.Factories
return true; return true;
} }
} }
} }
@@ -256,10 +256,10 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 7, DataTypeId = -92, PropertyEditorAlias = Constants.PropertyEditors.NoEditAlias, DbType = "Nvarchar" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 7, DataTypeId = -92, PropertyEditorAlias = Constants.PropertyEditors.NoEditAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 8, DataTypeId = -36, PropertyEditorAlias = Constants.PropertyEditors.DateTimeAlias, DbType = "Date" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 8, DataTypeId = -36, PropertyEditorAlias = Constants.PropertyEditors.DateTimeAlias, DbType = "Date" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, PropertyEditorAlias = Constants.PropertyEditors.ColorPickerAlias, DbType = "Nvarchar" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 9, DataTypeId = -37, PropertyEditorAlias = Constants.PropertyEditors.ColorPickerAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 11, DataTypeId = -39, PropertyEditorAlias = Constants.PropertyEditors.DropDownListFlexibleAlias, DbType = "Nvarchar" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 11, DataTypeId = -39, PropertyEditorAlias = Constants.PropertyEditors.DropDownListMultipleAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 12, DataTypeId = -40, PropertyEditorAlias = Constants.PropertyEditors.RadioButtonListAlias, DbType = "Nvarchar" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 12, DataTypeId = -40, PropertyEditorAlias = Constants.PropertyEditors.RadioButtonListAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 13, DataTypeId = -41, PropertyEditorAlias = Constants.PropertyEditors.DateAlias, DbType = "Date" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 13, DataTypeId = -41, PropertyEditorAlias = Constants.PropertyEditors.DateAlias, DbType = "Date" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 14, DataTypeId = -42, PropertyEditorAlias = Constants.PropertyEditors.DropDownListFlexibleAlias, DbType = "Integer" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 14, DataTypeId = -42, PropertyEditorAlias = Constants.PropertyEditors.DropDownListAlias, DbType = "Integer" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 15, DataTypeId = -43, PropertyEditorAlias = Constants.PropertyEditors.CheckBoxListAlias, DbType = "Nvarchar" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 15, DataTypeId = -43, PropertyEditorAlias = Constants.PropertyEditors.CheckBoxListAlias, DbType = "Nvarchar" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 22, DataTypeId = 1041, PropertyEditorAlias = Constants.PropertyEditors.TagsAlias, DbType = "Ntext" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 22, DataTypeId = 1041, PropertyEditorAlias = Constants.PropertyEditors.TagsAlias, DbType = "Ntext" });
_database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 24, DataTypeId = 1043, PropertyEditorAlias = Constants.PropertyEditors.ImageCropperAlias, DbType = "Ntext" }); _database.Insert("cmsDataType", "pk", false, new DataTypeDto { PrimaryKey = 24, DataTypeId = 1043, PropertyEditorAlias = Constants.PropertyEditors.ImageCropperAlias, DbType = "Ntext" });
@@ -300,12 +300,6 @@ namespace Umbraco.Core.Persistence.Migrations.Initial
//default's for MultipleMediaPickerAlias picker //default's for MultipleMediaPickerAlias picker
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 6, Alias = "multiPicker", SortOrder = 0, DataTypeNodeId = 1049, Value = "1" }); _database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 6, Alias = "multiPicker", SortOrder = 0, DataTypeNodeId = 1049, Value = "1" });
// Defaults for single item dropdown
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 7, Alias = "multiple", SortOrder = 0, DataTypeNodeId = -42, Value = "0" });
// Defaults for multiple item dropdown
_database.Insert("cmsDataTypePreValues", "id", false, new DataTypePreValueDto { Id = 8, Alias = "multiple", SortOrder = 0, DataTypeNodeId = -39, Value = "1" });
} }
private void CreateUmbracoRelationTypeData() private void CreateUmbracoRelationTypeData()
@@ -1,5 +1,4 @@
using System.Data; using System.Data;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
@@ -140,12 +139,10 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
{ {
Name = indexName, Name = indexName,
SchemaName = Expression.SchemaName, SchemaName = Expression.SchemaName,
TableName = Expression.TableName, TableName = Expression.TableName,
IndexType = IndexTypes.UniqueNonClustered IsUnique = true
}); });
index.Index.Columns.Add(new IndexColumnDefinition index.Index.Columns.Add(new IndexColumnDefinition
{ {
Name = Expression.Column.Name Name = Expression.Column.Name
@@ -245,4 +242,4 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Column
return this; return this;
} }
} }
} }
@@ -1,5 +1,4 @@
using System.Data; using System.Data;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Alter.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
@@ -125,8 +124,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
{ {
Name = indexName, Name = indexName,
SchemaName = Expression.SchemaName, SchemaName = Expression.SchemaName,
TableName = Expression.TableName, TableName = Expression.TableName,
IndexType = IndexTypes.UniqueNonClustered IsUnique = true
}); });
index.Index.Columns.Add(new IndexColumnDefinition index.Index.Columns.Add(new IndexColumnDefinition
@@ -260,4 +259,4 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Alter.Table
return this; return this;
} }
} }
} }
@@ -1,5 +1,4 @@
using System.Data; using System.Data;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
using Umbraco.Core.Persistence.SqlSyntax; using Umbraco.Core.Persistence.SqlSyntax;
@@ -115,8 +114,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column
{ {
Name = indexName, Name = indexName,
SchemaName = Expression.SchemaName, SchemaName = Expression.SchemaName,
TableName = Expression.TableName, TableName = Expression.TableName,
IndexType = IndexTypes.UniqueNonClustered IsUnique = true
}); });
index.Index.Columns.Add(new IndexColumnDefinition index.Index.Columns.Add(new IndexColumnDefinition
@@ -218,4 +217,4 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Column
return this; return this;
} }
} }
} }
@@ -47,27 +47,39 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Index
} }
ICreateIndexOnColumnSyntax ICreateIndexColumnOptionsSyntax.Unique() ICreateIndexOnColumnSyntax ICreateIndexColumnOptionsSyntax.Unique()
{ {
Expression.Index.IndexType = IndexTypes.UniqueNonClustered; Expression.Index.IsUnique = true;
//if it is Unique then it must be unique nonclustered and set the other flags
Expression.Index.IndexType = IndexTypes.UniqueNonClustered;
Expression.Index.IsClustered = false;
return this; return this;
} }
public ICreateIndexOnColumnSyntax NonClustered() public ICreateIndexOnColumnSyntax NonClustered()
{ {
Expression.Index.IndexType = IndexTypes.NonClustered; Expression.Index.IndexType = IndexTypes.NonClustered;
Expression.Index.IsClustered = false;
Expression.Index.IndexType = IndexTypes.NonClustered;
Expression.Index.IsUnique = false;
return this; return this;
} }
public ICreateIndexOnColumnSyntax Clustered() public ICreateIndexOnColumnSyntax Clustered()
{ {
Expression.Index.IndexType = IndexTypes.Clustered; Expression.Index.IndexType = IndexTypes.Clustered;
return this; Expression.Index.IsClustered = true;
//if it is clustered then we have to change the index type set the other flags
Expression.Index.IndexType = IndexTypes.Clustered;
Expression.Index.IsClustered = true;
Expression.Index.IsUnique = false;
return this;
} }
ICreateIndexOnColumnSyntax ICreateIndexOptionsSyntax.Unique() ICreateIndexOnColumnSyntax ICreateIndexOptionsSyntax.Unique()
{ {
Expression.Index.IndexType = IndexTypes.UniqueNonClustered; Expression.Index.IndexType = IndexTypes.UniqueNonClustered;
Expression.Index.IsUnique = true;
return this; return this;
} }
} }
} }
@@ -1,5 +1,4 @@
using System.Data; using System.Data;
using Umbraco.Core.Persistence.DatabaseAnnotations;
using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Create.Expressions;
using Umbraco.Core.Persistence.Migrations.Syntax.Expressions; using Umbraco.Core.Persistence.Migrations.Syntax.Expressions;
@@ -165,8 +164,8 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
{ {
Name = indexName, Name = indexName,
SchemaName = Expression.SchemaName, SchemaName = Expression.SchemaName,
TableName = Expression.TableName, TableName = Expression.TableName,
IndexType = IndexTypes.UniqueNonClustered IsUnique = true
}); });
index.Index.Columns.Add(new IndexColumnDefinition index.Index.Columns.Add(new IndexColumnDefinition
@@ -268,4 +267,4 @@ namespace Umbraco.Core.Persistence.Migrations.Syntax.Create.Table
return this; return this;
} }
} }
} }
@@ -48,7 +48,7 @@ namespace Umbraco.Core.Persistence.Repositories
var sql = GetBaseQuery(false); var sql = GetBaseQuery(false);
if (query == null) query = new Query<IAuditItem>(); if (query == null) query = new Query<IAuditItem>();
var queryHasWhereClause = query.GetWhereClauses().Any(); var queryHasWhereClause = query.GetWhereClauses().Any();
var translatorIds = new SqlTranslator<IAuditItem>(sql, query); var translatorIds = new SqlTranslator<IAuditItem>(sql, query);
var translatedQuery = translatorIds.Translate(); var translatedQuery = translatorIds.Translate();
@@ -59,7 +59,7 @@ namespace Umbraco.Core.Persistence.Repositories
{ {
var filterSql = new Sql(); var filterSql = new Sql();
foreach (var filterClause in customFilterWheres) foreach (var filterClause in customFilterWheres)
{ {
filterSql.Append($"AND ({filterClause.Item1})", filterClause.Item2); filterSql.Append($"AND ({filterClause.Item1})", filterClause.Item2);
} }
@@ -70,7 +70,7 @@ namespace Umbraco.Core.Persistence.Repositories
{ {
var filterSql = new Sql(); var filterSql = new Sql();
foreach (var filterClause in auditTypeFilter) foreach (var filterClause in auditTypeFilter)
{ {
filterSql.Append("AND (logHeader = @logHeader)", new { logHeader = filterClause.ToString() }); filterSql.Append("AND (logHeader = @logHeader)", new { logHeader = filterClause.ToString() });
} }
@@ -80,14 +80,14 @@ namespace Umbraco.Core.Persistence.Repositories
if (orderDirection == Direction.Descending) if (orderDirection == Direction.Descending)
translatedQuery.OrderByDescending("Datestamp"); translatedQuery.OrderByDescending("Datestamp");
else else
translatedQuery.OrderBy("Datestamp"); translatedQuery.OrderBy("Datestamp");
// Get page of results and total count // Get page of results and total count
var pagedResult = Database.Page<LogDto>(pageIndex + 1, pageSize, translatedQuery); var pagedResult = Database.Page<LogDto>(pageIndex + 1, pageSize, translatedQuery);
totalRecords = pagedResult.TotalItems; totalRecords = pagedResult.TotalItems;
var pages = pagedResult.Items.Select( var pages = pagedResult.Items.Select(
dto => new AuditItem(dto.NodeId, dto.Comment, Enum<AuditType>.ParseOrNull(dto.Header) ?? AuditType.Custom, dto.UserId)).ToArray(); dto => new AuditItem(dto.Id, dto.Comment, Enum<AuditType>.ParseOrNull(dto.Header) ?? AuditType.Custom, dto.UserId)).ToArray();
//Mapping the DateStamp //Mapping the DateStamp
for (var i = 0; i < pages.Length; i++) for (var i = 0; i < pages.Length; i++)
@@ -142,7 +142,7 @@ namespace Umbraco.Core.Persistence.Repositories
protected override IEnumerable<IAuditItem> PerformGetByQuery(IQuery<IAuditItem> query) protected override IEnumerable<IAuditItem> PerformGetByQuery(IQuery<IAuditItem> query)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
protected override string GetBaseWhereClause() protected override string GetBaseWhereClause()
{ {
@@ -167,7 +167,7 @@ namespace Umbraco.Core.Persistence.Repositories
// Apply filter // Apply filter
if (filterSql != null) if (filterSql != null)
{ {
//ensure we don't append a WHERE if there is already one //ensure we don't append a WHERE if there is already one
var sqlFilter = hasWhereClause var sqlFilter = hasWhereClause
? filterSql.SQL ? filterSql.SQL
: " WHERE " + filterSql.SQL.TrimStart("AND "); : " WHERE " + filterSql.SQL.TrimStart("AND ");
@@ -1,4 +1,4 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
@@ -64,8 +64,7 @@ namespace Umbraco.Core.Persistence.Repositories
}, objectTypeId); }, objectTypeId);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query); var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var entitySql = translator.Translate(); var entitySql = translator.Translate();
var pagedSql = entitySql.Append(GetGroupBy(isContent, isMedia, false)); var pagedSql = entitySql.Append(GetGroupBy(isContent, isMedia, false)).OrderBy("umbracoNode.id");
pagedSql = (orderDirection == Direction.Descending) ? pagedSql.OrderByDescending("umbracoNode.id") : pagedSql.OrderBy("umbracoNode.id");
IEnumerable<IUmbracoEntity> result; IEnumerable<IUmbracoEntity> result;
@@ -81,8 +80,8 @@ namespace Umbraco.Core.Persistence.Repositories
foreach (var idGroup in ids) foreach (var idGroup in ids)
{ {
var propSql = GetPropertySql(Constants.ObjectTypes.Media) var propSql = GetPropertySql(Constants.ObjectTypes.Media)
.Where("contentNodeId IN (@ids)", new { ids = idGroup }); .Where("contentNodeId IN (@ids)", new {ids = idGroup})
propSql = (orderDirection == Direction.Descending) ? propSql.OrderByDescending("contentNodeId") : propSql.OrderBy("contentNodeId"); .OrderBy("contentNodeId");
//This does NOT fetch all data into memory in a list, this will read //This does NOT fetch all data into memory in a list, this will read
// over the records as a data reader, this is much better for performance and memory, // over the records as a data reader, this is much better for performance and memory,
@@ -332,23 +331,14 @@ namespace Umbraco.Core.Persistence.Repositories
return list; return list;
} }
/// <summary>
/// Gets entities by query.
/// </summary>
/// <param name="query"></param>
/// <param name="objectTypeId"></param>
/// <returns></returns>
/// <remarks>
/// Note that this will also fetch all property data for media items, which can cause performance problems
/// when used without paging, in sites with large amounts of data in cmsPropertyData.
/// </remarks>
public virtual IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId) public virtual IEnumerable<IUmbracoEntity> GetByQuery(IQuery<IUmbracoEntity> query, Guid objectTypeId)
{ {
var isContent = objectTypeId == Constants.ObjectTypes.DocumentGuid || objectTypeId == Constants.ObjectTypes.DocumentBlueprintGuid;
var isMedia = objectTypeId == Constants.ObjectTypes.MediaGuid; bool isContent = objectTypeId == Constants.ObjectTypes.DocumentGuid || objectTypeId == Constants.ObjectTypes.DocumentBlueprintGuid;
bool isMedia = objectTypeId == Constants.ObjectTypes.MediaGuid;
var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, null, objectTypeId); var sqlClause = GetBaseWhere(GetBase, isContent, isMedia, null, objectTypeId);
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query); var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var entitySql = translator.Translate(); var entitySql = translator.Translate();
@@ -374,49 +364,22 @@ namespace Umbraco.Core.Persistence.Repositories
} }
else else
{ {
return GetByQueryInternal(entitySql, isContent, isMedia); //use dynamic so that we can get ALL properties from the SQL so we can chuck that data into our AdditionalData
var finalSql = entitySql.Append(GetGroupBy(isContent, false));
//query = read forward data reader, do not load everything into mem
var dtos = _work.Database.Query<dynamic>(finalSql);
var collection = new EntityDefinitionCollection();
foreach (var dto in dtos)
{
collection.AddOrUpdate(new EntityDefinition(factory, dto, isContent, false));
}
return collection.Select(x => x.BuildFromDynamic()).ToList();
} }
} }
/// <summary>
/// Gets entities by query without fetching property data.
/// </summary>
/// <param name="query"></param>
/// <param name="objectTypeId"></param>
/// <returns></returns>
/// <remarks>
/// This is supposed to be internal and can be used when getting all entities without paging, without causing
/// performance issues.
/// </remarks>
internal IEnumerable<IUmbracoEntity> GetMediaByQueryWithoutPropertyData(IQuery<IUmbracoEntity> query)
{
var sqlClause = GetBaseWhere(GetBase, false, true, null, UmbracoObjectTypes.Media.GetGuid());
var translator = new SqlTranslator<IUmbracoEntity>(sqlClause, query);
var entitySql = translator.Translate();
return GetByQueryInternal(entitySql, false, true);
}
internal IEnumerable<IUmbracoEntity> GetByQueryInternal(Sql entitySql, bool isContent, bool isMedia)
{
var factory = new UmbracoEntityFactory();
//use dynamic so that we can get ALL properties from the SQL so we can chuck that data into our AdditionalData
var finalSql = entitySql.Append(GetGroupBy(isContent, isMedia));
//query = read forward data reader, do not load everything into mem
var dtos = _work.Database.Query<dynamic>(finalSql);
var collection = new EntityDefinitionCollection();
foreach (var dto in dtos)
{
collection.AddOrUpdate(new EntityDefinition(factory, dto, isContent, isMedia));
}
return collection.Select(x => x.BuildFromDynamic()).ToList();
}
#endregion #endregion
#region Sql Statements #region Sql Statements
@@ -888,4 +851,4 @@ namespace Umbraco.Core.Persistence.Repositories
} }
#endregion #endregion
} }
} }
@@ -258,20 +258,12 @@ namespace Umbraco.Core.Persistence.Repositories
//.Where(x => Equals(x, default(TId)) == false) //.Where(x => Equals(x, default(TId)) == false)
.ToArray(); .ToArray();
// can't query more than 2000 ids at a time... but if someone is really querying 2000+ entities, if (ids.Length > 2000)
// the additional overhead of fetching them in groups is minimal compared to the lookup time of each group
const int maxParams = 2000;
if (ids.Length <= maxParams)
{ {
return CachePolicy.GetAll(ids, PerformGetAll); throw new InvalidOperationException("Cannot perform a query with more than 2000 parameters");
} }
var entities = new List<TEntity>();
foreach (var groupOfIds in ids.InGroupsOf(maxParams))
{
entities.AddRange(CachePolicy.GetAll(groupOfIds.ToArray(), PerformGetAll));
}
return entities;
return CachePolicy.GetAll(ids, PerformGetAll);
} }
protected abstract IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query); protected abstract IEnumerable<TEntity> PerformGetByQuery(IQuery<TEntity> query);
@@ -152,7 +152,7 @@ namespace Umbraco.Core.Persistence.Repositories
public IProfile GetProfile(string username) public IProfile GetProfile(string username)
{ {
var sql = GetBaseQuery(false).Where<UserDto>(userDto => userDto.Login == username, SqlSyntax); var sql = GetBaseQuery(false).Where<UserDto>(userDto => userDto.UserName == username, SqlSyntax);
var dto = Database.Fetch<UserDto>(sql) var dto = Database.Fetch<UserDto>(sql)
.FirstOrDefault(); .FirstOrDefault();
@@ -382,7 +382,6 @@ ORDER BY colName";
"DELETE FROM cmsTask WHERE parentUserId = @Id", "DELETE FROM cmsTask WHERE parentUserId = @Id",
"DELETE FROM umbracoUser2UserGroup WHERE userId = @Id", "DELETE FROM umbracoUser2UserGroup WHERE userId = @Id",
"DELETE FROM umbracoUser2NodeNotify WHERE userId = @Id", "DELETE FROM umbracoUser2NodeNotify WHERE userId = @Id",
"DELETE FROM umbracoUserStartNode WHERE userId = @Id",
"DELETE FROM umbracoUser WHERE id = @Id", "DELETE FROM umbracoUser WHERE id = @Id",
"DELETE FROM umbracoExternalLogin WHERE id = @Id" "DELETE FROM umbracoExternalLogin WHERE id = @Id"
}; };
@@ -385,16 +385,10 @@ namespace Umbraco.Core.Persistence.Repositories
if (orderDirection == Direction.Descending) if (orderDirection == Direction.Descending)
{ {
sortedSql.OrderByDescending("CustomPropData.CustomPropVal"); sortedSql.OrderByDescending("CustomPropData.CustomPropVal");
// need to ensure ordering unique by using id as CustomPropVal may not be unique
// see: https://github.com/umbraco/Umbraco-CMS/issues/3296
sortedSql.OrderByDescending("umbracoNode.id");
} }
else else
{ {
sortedSql.OrderBy("CustomPropData.CustomPropVal"); sortedSql.OrderBy("CustomPropData.CustomPropVal");
// need to ensure ordering unique by using id as CustomPropVal may not be unique
// see: https://github.com/umbraco/Umbraco-CMS/issues/3296
sortedSql.OrderBy("umbracoNode.id");
} }
} }
@@ -333,9 +333,14 @@ ORDER BY TABLE_NAME, INDEX_NAME",
switch (systemMethod) switch (systemMethod)
{ {
case SystemMethods.NewGuid: case SystemMethods.NewGuid:
return null; // NOT SUPPORTED! return null; // NOT SUPPORTED!
//return "NEWID()";
case SystemMethods.CurrentDateTime: case SystemMethods.CurrentDateTime:
return "CURRENT_TIMESTAMP"; return "CURRENT_TIMESTAMP";
//case SystemMethods.NewSequentialId:
// return "NEWSEQUENTIALID()";
//case SystemMethods.CurrentUTCDateTime:
// return "GETUTCDATE()";
} }
return null; return null;
+1 -11
View File
@@ -2575,18 +2575,8 @@ namespace Umbraco.Core.Services
_publishingStrategy.PublishingFinalized(uow, descendants, false); _publishingStrategy.PublishingFinalized(uow, descendants, false);
} }
Audit(uow, AuditType.Publish, "Save and Publish performed by user", userId, content.Id);
uow.Commit(); uow.Commit();
if (publishStatus.StatusType == PublishStatusType.Success)
{
Audit(uow, AuditType.Publish, "Save and Publish performed by user", userId, content.Id);
}
else
{
Audit(uow, AuditType.Save, "Save performed by user", userId, content.Id);
}
uow.Commit();
return Attempt.If(publishStatus.StatusType == PublishStatusType.Success, publishStatus); return Attempt.If(publishStatus.StatusType == PublishStatusType.Success, publishStatus);
} }
} }
+7 -25
View File
@@ -2,6 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Text;
using Umbraco.Core.Cache;
using Umbraco.Core.CodeAnnotations; using Umbraco.Core.CodeAnnotations;
using Umbraco.Core.Events; using Umbraco.Core.Events;
using Umbraco.Core.Logging; using Umbraco.Core.Logging;
@@ -11,7 +13,6 @@ using Umbraco.Core.Models.Rdbms;
using Umbraco.Core.Persistence; using Umbraco.Core.Persistence;
using Umbraco.Core.Persistence.DatabaseModelDefinitions; using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using Umbraco.Core.Persistence.Querying; using Umbraco.Core.Persistence.Querying;
using Umbraco.Core.Persistence.Repositories;
using Umbraco.Core.Persistence.UnitOfWork; using Umbraco.Core.Persistence.UnitOfWork;
namespace Umbraco.Core.Services namespace Umbraco.Core.Services
@@ -286,35 +287,16 @@ namespace Umbraco.Core.Services
public virtual IEnumerable<IUmbracoEntity> GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType) public virtual IEnumerable<IUmbracoEntity> GetChildren(int parentId, UmbracoObjectTypes umbracoObjectType)
{ {
var objectTypeId = umbracoObjectType.GetGuid(); var objectTypeId = umbracoObjectType.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly: true)) using (var uow = UowProvider.GetUnitOfWork(readOnly:true))
{
var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId);
return repository.GetByQuery(query, objectTypeId);
}
}
/// <summary>
/// Gets a collection of children by the parent's Id and UmbracoObjectType without adding property data
/// </summary>
/// <param name="parentId">Id of the parent to retrieve children for</param>
/// <returns>An enumerable list of <see cref="IUmbracoEntity"/> objects</returns>
internal IEnumerable<IUmbracoEntity> GetMediaChildrenWithoutPropertyData(int parentId)
{
var objectTypeId = UmbracoObjectTypes.Media.GetGuid();
using (var uow = UowProvider.GetUnitOfWork(readOnly: true))
{ {
var repository = RepositoryFactory.CreateEntityRepository(uow); var repository = RepositoryFactory.CreateEntityRepository(uow);
var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId); var query = Query<IUmbracoEntity>.Builder.Where(x => x.ParentId == parentId);
// Not pretty having to cast the repository, but it is the only way to get to use an internal method that we var contents = repository.GetByQuery(query, objectTypeId);
// do not want to make public on the interface. Unfortunately also prevents this from being unit tested. return contents;
// See this issue for details on why we need this:
// https://github.com/umbraco/Umbraco-CMS/issues/3457
return ((EntityRepository)repository).GetMediaByQueryWithoutPropertyData(query);
} }
} }
/// <summary> /// <summary>
/// Returns a paged collection of children /// Returns a paged collection of children
/// </summary> /// </summary>
@@ -799,4 +781,4 @@ namespace Umbraco.Core.Services
return node.NodeId; return node.NodeId;
} }
} }
} }
@@ -26,15 +26,6 @@ namespace Umbraco.Core.Services
/// <param name="userId">Id of the User deleting the Media</param> /// <param name="userId">Id of the User deleting the Media</param>
Attempt<OperationStatus> MoveToRecycleBin(IMedia media, int userId = 0); Attempt<OperationStatus> MoveToRecycleBin(IMedia media, int userId = 0);
/// <summary>
/// Moves an <see cref="IMedia"/> object to a new location
/// </summary>
/// <param name="media">The <see cref="IMedia"/> to move</param>
/// <param name="parentId">Id of the Media's new Parent</param>
/// <param name="userId">Id of the User moving the Media</param>
/// <returns>True if moving succeeded, otherwise False</returns>
Attempt<OperationStatus> Move(IMedia media, int parentId, int userId = 0);
/// <summary> /// <summary>
/// Permanently deletes an <see cref="IMedia"/> object /// Permanently deletes an <see cref="IMedia"/> object
/// </summary> /// </summary>
@@ -278,7 +269,6 @@ namespace Umbraco.Core.Services
/// <param name="userId">Id of the User moving the Media</param> /// <param name="userId">Id of the User moving the Media</param>
void Move(IMedia media, int parentId, int userId = 0); void Move(IMedia media, int parentId, int userId = 0);
/// <summary> /// <summary>
/// Deletes an <see cref="IMedia"/> object by moving it to the Recycle Bin /// Deletes an <see cref="IMedia"/> object by moving it to the Recycle Bin
/// </summary> /// </summary>
+7 -15
View File
@@ -465,7 +465,7 @@ namespace Umbraco.Core.Services
return repository.GetByQuery(query); return repository.GetByQuery(query);
} }
} }
[Obsolete("Use the overload with 'long' parameter types instead")] [Obsolete("Use the overload with 'long' parameter types instead")]
[EditorBrowsable(EditorBrowsableState.Never)] [EditorBrowsable(EditorBrowsableState.Never)]
public IEnumerable<IMedia> GetPagedChildren(int id, int pageIndex, int pageSize, out int totalChildren, public IEnumerable<IMedia> GetPagedChildren(int id, int pageIndex, int pageSize, out int totalChildren,
@@ -758,20 +758,19 @@ namespace Umbraco.Core.Services
/// <param name="media">The <see cref="IMedia"/> to move</param> /// <param name="media">The <see cref="IMedia"/> to move</param>
/// <param name="parentId">Id of the Media's new Parent</param> /// <param name="parentId">Id of the Media's new Parent</param>
/// <param name="userId">Id of the User moving the Media</param> /// <param name="userId">Id of the User moving the Media</param>
/// <returns>True if moving succeeded, otherwise False</returns> public void Move(IMedia media, int parentId, int userId = 0)
public Attempt<OperationStatus> Move(IMedia media, int parentId, int userId = 0)
{ {
//TODO: This all needs to be on the repo layer in one transaction! //TODO: This all needs to be on the repo layer in one transaction!
if (media == null) throw new ArgumentNullException("media"); if (media == null) throw new ArgumentNullException("media");
var evtMsgs = EventMessagesFactory.Get();
using (new WriteLock(Locker)) using (new WriteLock(Locker))
{ {
//This ensures that the correct method is called if this method is used to Move to recycle bin. //This ensures that the correct method is called if this method is used to Move to recycle bin.
if (parentId == Constants.System.RecycleBinMedia) if (parentId == Constants.System.RecycleBinMedia)
{ {
MoveToRecycleBin(media, userId); MoveToRecycleBin(media, userId);
return OperationStatus.Success(evtMsgs); return;
} }
using (var uow = UowProvider.GetUnitOfWork()) using (var uow = UowProvider.GetUnitOfWork())
@@ -779,11 +778,11 @@ namespace Umbraco.Core.Services
var originalPath = media.Path; var originalPath = media.Path;
var moveEventInfo = new MoveEventInfo<IMedia>(media, originalPath, parentId); var moveEventInfo = new MoveEventInfo<IMedia>(media, originalPath, parentId);
var moveEventArgs = new MoveEventArgs<IMedia>(true, evtMsgs, moveEventInfo); var moveEventArgs = new MoveEventArgs<IMedia>(moveEventInfo);
if (uow.Events.DispatchCancelable(Moving, this, moveEventArgs, "Moving")) if (uow.Events.DispatchCancelable(Moving, this, moveEventArgs, "Moving"))
{ {
uow.Commit(); uow.Commit();
return OperationStatus.Cancelled(evtMsgs); ; return;
} }
media.ParentId = parentId; media.ParentId = parentId;
@@ -817,8 +816,6 @@ namespace Umbraco.Core.Services
Audit(uow, AuditType.Move, "Move Media performed by user", userId, media.Id); Audit(uow, AuditType.Move, "Move Media performed by user", userId, media.Id);
uow.Commit(); uow.Commit();
} }
return OperationStatus.Success(evtMsgs);
} }
} }
@@ -1104,7 +1101,7 @@ namespace Umbraco.Core.Services
media.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate); media.EnsureValidPath(Logger, entity => GetById(entity.ParentId), QuickUpdate);
var originalPath = media.Path; var originalPath = media.Path;
var moveEventInfo = new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia); var moveEventInfo = new MoveEventInfo<IMedia>(media, originalPath, Constants.System.RecycleBinMedia);
var moveEventArgs = new MoveEventArgs<IMedia>(true, evtMsgs, moveEventInfo); var moveEventArgs = new MoveEventArgs<IMedia>(moveEventInfo);
if (uow.Events.DispatchCancelable(Trashing, this, moveEventArgs, "Trashing")) if (uow.Events.DispatchCancelable(Trashing, this, moveEventArgs, "Trashing"))
{ {
uow.Commit(); uow.Commit();
@@ -1471,11 +1468,6 @@ namespace Umbraco.Core.Services
} }
} }
void IMediaService.Move(IMedia media, int parentId, int userId)
{
Move(media, parentId, userId);
}
#region Event Handlers #region Event Handlers
@@ -58,7 +58,54 @@ namespace Umbraco.Core.Services
if (entity is IContent == false) if (entity is IContent == false)
throw new NotSupportedException(); throw new NotSupportedException();
SendNotifications(operatingUser, new[] { (IContent)entity }, action, actionName, http, createSubject, createBody); var content = (IContent) entity;
// lazily get previous version
IContentBase prevVersion = null;
// do not load *all* users in memory at once
// do not load notifications *per user* (N+1 select)
// cannot load users & notifications in 1 query (combination btw User2AppDto and User2NodeNotifyDto)
// => get batches of users, get all their notifications in 1 query
// re. users:
// users being (dis)approved = not an issue, filtered in memory not in SQL
// users being modified or created = not an issue, ordering by ID, as long as we don't *insert* low IDs
// users being deleted = not an issue for GetNextUsers
var id = 0;
var nodeIds = content.Path.Split(',').Select(int.Parse).ToArray();
const int pagesz = 400; // load batches of 400 users
do
{
// users are returned ordered by id, notifications are returned ordered by user id
var users = ((UserService) _userService).GetNextUsers(id, pagesz).Where(x => x.IsApproved).ToList();
var notifications = GetUsersNotifications(users.Select(x => x.Id), action, nodeIds, Constants.ObjectTypes.DocumentGuid).ToList();
if (notifications.Count == 0) break;
var i = 0;
foreach (var user in users)
{
// continue if there's no notification for this user
if (notifications[i].UserId != user.Id) continue; // next user
// lazy load prev version
if (prevVersion == null)
{
prevVersion = GetPreviousVersion(entity.Id);
}
// queue notification
var req = CreateNotificationRequest(operatingUser, user, content, prevVersion, actionName, http, createSubject, createBody);
Enqueue(req);
// skip other notifications for this user
while (i < notifications.Count && notifications[i++].UserId == user.Id) ;
if (i >= notifications.Count) break; // break if no more notifications
}
// load more users if any
id = users.Count == pagesz ? users.Last().Id + 1 : -1;
} while (id > 0);
} }
/// <summary> /// <summary>
@@ -107,14 +154,7 @@ namespace Umbraco.Core.Services
// lazily get versions // lazily get versions
var prevVersionDictionary = new Dictionary<int, IContentBase>(); var prevVersionDictionary = new Dictionary<int, IContentBase>();
// do not load *all* users in memory at once // see notes above
// do not load notifications *per user* (N+1 select)
// cannot load users & notifications in 1 query (combination btw User2AppDto and User2NodeNotifyDto)
// => get batches of users, get all their notifications in 1 query
// re. users:
// users being (dis)approved = not an issue, filtered in memory not in SQL
// users being modified or created = not an issue, ordering by ID, as long as we don't *insert* low IDs
// users being deleted = not an issue for GetNextUsers
var id = 0; var id = 0;
const int pagesz = 400; // load batches of 400 users const int pagesz = 400; // load batches of 400 users
do do
@@ -616,4 +656,4 @@ namespace Umbraco.Core.Services
#endregion #endregion
} }
} }
+1 -1
View File
@@ -53,7 +53,7 @@ namespace Umbraco.Core
.Reverse() .Reverse()
.ToArray(); .ToArray();
return nodeIds; return nodeIds;
} }
/// <summary> /// <summary>
/// Removes new lines and tabs /// Removes new lines and tabs
@@ -35,7 +35,7 @@ namespace Umbraco.Tests.Migrations.Upgrades
catch (Exception) catch (Exception)
{ {
//if this doesn't work we have to make sure everything is reset! otherwise //if this doesn't work we have to make sure everything is reset! otherwise
// we'll run into issues because we've already set some things up // well run into issues because we've already set some things up
TearDown(); TearDown();
throw; throw;
} }
@@ -83,4 +83,4 @@ namespace Umbraco.Tests.Migrations.Upgrades
return SqlScripts.SqlResources.SqlCeTotal_480; return SqlScripts.SqlResources.SqlCeTotal_480;
} }
} }
} }
@@ -89,7 +89,7 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine,
var sqlSyntax = new SqlServerSyntaxProvider(); var sqlSyntax = new SqlServerSyntaxProvider();
var indexDefinition = CreateIndexDefinition(); var indexDefinition = CreateIndexDefinition();
indexDefinition.IndexType = IndexTypes.Clustered; indexDefinition.IsClustered = false;
var actual = sqlSyntax.Format(indexDefinition); var actual = sqlSyntax.Format(indexDefinition);
Assert.AreEqual("CREATE CLUSTERED INDEX [IX_A] ON [TheTable] ([A])", actual); Assert.AreEqual("CREATE CLUSTERED INDEX [IX_A] ON [TheTable] ([A])", actual);
@@ -159,4 +159,4 @@ WHERE (([umbracoNode].[nodeObjectType] = @0))) x)".Replace(Environment.NewLine,
} }
} }
} }
@@ -183,8 +183,8 @@ namespace Umbraco.Tests.TestHelpers
{ {
RemoveDatabaseFile(ex => RemoveDatabaseFile(ex =>
{ {
//If this doesn't work we have to make sure everything is reset! otherwise //if this doesn't work we have to make sure everything is reset! otherwise
// we'll run into issues because we've already set some things up // well run into issues because we've already set some things up
TearDown(); TearDown();
throw ex; throw ex;
}); });
@@ -435,4 +435,4 @@ namespace Umbraco.Tests.TestHelpers
</root>"; </root>";
} }
} }
} }
+6 -9
View File
@@ -31,10 +31,7 @@
"moment": "~2.10.3", "moment": "~2.10.3",
"ace-builds": "^1.2.3", "ace-builds": "^1.2.3",
"clipboard": "1.7.1", "clipboard": "1.7.1",
"font-awesome": "~4.7" "font-awesome": "~4.2"
},
"resolutions": {
"font-awesome": "~4.7"
}, },
"install": { "install": {
"path": "lib-bower", "path": "lib-bower",
@@ -46,11 +43,11 @@
], ],
"sources": { "sources": {
"moment": [ "moment": [
"bower_components/moment/min/moment.min.js", "bower_components/moment/min/moment.min.js",
"bower_components/moment/min/moment-with-locales.js", "bower_components/moment/min/moment-with-locales.js",
"bower_components/moment/min/moment-with-locales.min.js", "bower_components/moment/min/moment-with-locales.min.js",
"bower_components/moment/locale/*.js" "bower_components/moment/locale/*.js"
], ],
"underscore": [ "underscore": [
"bower_components/underscore/underscore-min.js", "bower_components/underscore/underscore-min.js",
"bower_components/underscore/underscore-min.map" "bower_components/underscore/underscore-min.map"
@@ -150,8 +150,7 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
sourceUrl: currentMenuNode.childNodesUrl, sourceUrl: currentMenuNode.childNodesUrl,
updateDefinition: function() { updateDefinition: function() {
throw "'updateDefinition' method is not supported in Umbraco 7, consider upgrading to the new v7 APIs"; throw "'updateDefinition' method is not supported in Umbraco 7, consider upgrading to the new v7 APIs";
}, }
expanded: currentMenuNode.expanded === true
}; };
//defined getters that will throw a not implemented/supported exception //defined getters that will throw a not implemented/supported exception
Object.defineProperty(legacyNode, "menu", { Object.defineProperty(legacyNode, "menu", {
+4552 -5610
View File
File diff suppressed because it is too large Load Diff
+5 -7
View File
@@ -11,10 +11,10 @@
"cssnano": "^3.7.6", "cssnano": "^3.7.6",
"gulp": "^3.9.1", "gulp": "^3.9.1",
"gulp-concat": "^2.6.0", "gulp-concat": "^2.6.0",
"gulp-connect": "5.6.1", "gulp-connect": "5.0.0",
"gulp-eslint": "^5.0.0", "gulp-eslint": "^5.0.0",
"gulp-imagemin": "^4.1.0", "gulp-imagemin": "^4.1.0",
"gulp-less": "^4.0.1", "gulp-less": "^3.5.0",
"gulp-ngdocs": "^0.3.0", "gulp-ngdocs": "^0.3.0",
"gulp-open": "^2.1.0", "gulp-open": "^2.1.0",
"gulp-postcss": "^6.2.0", "gulp-postcss": "^6.2.0",
@@ -24,14 +24,12 @@
"gulp-wrap": "^0.13.0", "gulp-wrap": "^0.13.0",
"gulp-wrap-js": "^0.4.1", "gulp-wrap-js": "^0.4.1",
"jasmine-core": "2.5.2", "jasmine-core": "2.5.2",
"karma": "^3.1.3", "karma": "^1.7.0",
"karma-jasmine": "^1.1.1", "karma-jasmine": "^1.1.1",
"karma-phantomjs-launcher": "^1.0.4", "karma-phantomjs-launcher": "^1.0.4",
"less": "^3.9.0", "less": "^2.7.3",
"lodash": "^4.17.5", "lodash": "^4.17.5",
"merge-stream": "^1.0.1", "merge-stream": "^1.0.1",
"run-sequence": "^2.2.1", "run-sequence": "^2.2.1"
"marked": "^0.5.2",
"event-stream": "3.3.4"
} }
} }
@@ -252,7 +252,7 @@ Use this directive to construct a header inside the main editor window.
icon: "=", icon: "=",
hideIcon: "@", hideIcon: "@",
alias: "=", alias: "=",
hideAlias: "=", hideAlias: "@",
description: "=", description: "=",
hideDescription: "@", hideDescription: "@",
descriptionLocked: "@", descriptionLocked: "@",
@@ -1,5 +1,5 @@
angular.module("umbraco.directives") angular.module("umbraco.directives")
.directive('gridRte', function (tinyMceService, stylesheetResource, angularHelper, assetsService, $q, $timeout, eventsService) { .directive('gridRte', function (tinyMceService, stylesheetResource, angularHelper, assetsService, $q, $timeout) {
return { return {
scope: { scope: {
uniqueId: '=', uniqueId: '=',
@@ -362,23 +362,8 @@ angular.module("umbraco.directives")
// tinyMceEditor.fire('LoadContent', null); // tinyMceEditor.fire('LoadContent', null);
//}; //};
var tabShownListener = eventsService.on("app.tabChange", function (e, args) {
var tabId = args.id;
var myTabId = element.closest(".umb-tab-pane").attr("rel");
if (String(tabId) === myTabId) {
//the tab has been shown, trigger the mceAutoResize (as it could have timed out before the tab was shown)
if (tinyMceEditor !== undefined && tinyMceEditor != null) {
tinyMceEditor.execCommand('mceAutoResize', false, null, null);
}
}
});
//listen for formSubmitting event (the result is callback used to remove the event subscription) //listen for formSubmitting event (the result is callback used to remove the event subscription)
var formSubmittingListener = scope.$on("formSubmitting", function () { var unsubscribe = scope.$on("formSubmitting", function () {
//TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer //TODO: Here we should parse out the macro rendered content so we can save on a lot of bytes in data xfer
// we do parse it out on the server side but would be nice to do that on the client side before as well. // we do parse it out on the server side but would be nice to do that on the client side before as well.
scope.value = tinyMceEditor ? tinyMceEditor.getContent() : null; scope.value = tinyMceEditor ? tinyMceEditor.getContent() : null;
@@ -388,8 +373,7 @@ angular.module("umbraco.directives")
// NOTE: this is very important otherwise if this is part of a modal, the listener still exists because the dom // 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. // element might still be there even after the modal has been hidden.
scope.$on('$destroy', function () { scope.$on('$destroy', function () {
formSubmittingListener(); unsubscribe();
eventsService.unsubscribe(tabShownListener);
if (tinyMceEditor !== undefined && tinyMceEditor != null) { if (tinyMceEditor !== undefined && tinyMceEditor != null) {
tinyMceEditor.destroy() tinyMceEditor.destroy()
} }
@@ -110,7 +110,7 @@ angular.module("umbraco.directives")
scope.isCroppable = true; scope.isCroppable = true;
scope.hasDimensions = true; scope.hasDimensions = true;
if (scope.src) { if (scope.src !== "undefined") {
if (scope.src.endsWith(".svg")) { if (scope.src.endsWith(".svg")) {
scope.isCroppable = false; scope.isCroppable = false;
scope.hasDimensions = false; scope.hasDimensions = false;
@@ -135,19 +135,11 @@ angular.module("umbraco.directives")
} }
//is this the current action node (this is not the same as the current selected node!) //is this the current action node (this is not the same as the current selected node!)
var actionNode = appState.getMenuState("currentNode"); var actionNode = appState.getMenuState("currentNode");
if(actionNode) { if(actionNode) {
if(actionNode.id === node.id) {
if(actionNode.id === node.id && actionNode.id !== "-1") {
css.push("active"); css.push("active");
} }
// special handling of root nodes with id -1
// as there can be many nodes with id -1 in a tree we need to check the treeAlias instead
if(actionNode.id === "-1" && actionNode.metaData.treeAlias === node.metaData.treeAlias) {
css.push("active");
}
} }
return css.join(" "); return css.join(" ");
@@ -48,7 +48,7 @@ the directive will use {@link umbraco.directives.directive:umbLockedField umbLoc
**/ **/
angular.module("umbraco.directives") angular.module("umbraco.directives")
.directive('umbGenerateAlias', function ($timeout, entityResource, localizationService) { .directive('umbGenerateAlias', function ($timeout, entityResource) {
return { return {
restrict: 'E', restrict: 'E',
templateUrl: 'views/components/umb-generate-alias.html', templateUrl: 'views/components/umb-generate-alias.html',
@@ -67,21 +67,7 @@ angular.module("umbraco.directives")
var updateAlias = false; var updateAlias = false;
scope.locked = true; scope.locked = true;
scope.placeholderText = "Enter alias...";
scope.labels = {
idle: "Enter alias...",
busy: "Generating alias..."
};
scope.placeholderText = scope.labels.idle;
localizationService.localize('placeholders_enterAlias').then(function (value) {
scope.labels.idle = scope.placeholderText = value;
});
localizationService.localize('placeholders_generatingAlias').then(function (value) {
scope.labels.busy = value;
});
function generateAlias(value) { function generateAlias(value) {
@@ -92,7 +78,7 @@ angular.module("umbraco.directives")
if( value !== undefined && value !== "" && value !== null) { if( value !== undefined && value !== "" && value !== null) {
scope.alias = ""; scope.alias = "";
scope.placeholderText = scope.labels.busy; scope.placeholderText = "Generating Alias...";
generateAliasTimeout = $timeout(function () { generateAliasTimeout = $timeout(function () {
updateAlias = true; updateAlias = true;
@@ -106,7 +92,7 @@ angular.module("umbraco.directives")
} else { } else {
updateAlias = true; updateAlias = true;
scope.alias = ""; scope.alias = "";
scope.placeholderText = scope.labels.idle; scope.placeholderText = "Enter alias...";
} }
} }
@@ -13,8 +13,8 @@ function valTab() {
link: function (scope, element, attr, ctrs) { link: function (scope, element, attr, ctrs) {
var valFormManager = ctrs[1]; var valFormManager = ctrs[1];
var tabId = "tab" + scope.tab.id; var tabId = "tab" + scope.tab.id;
scope.tabHasError = false; scope.tabHasError = false;
//listen for form validation changes //listen for form validation changes
valFormManager.onValidationStatusChanged(function (evt, args) { valFormManager.onValidationStatusChanged(function (evt, args) {
@@ -31,7 +31,8 @@ function valTab() {
scope.tabHasError = false; scope.tabHasError = false;
} }
}); });
} }
}; };
} }
angular.module('umbraco.directives.validation').directive("valTab", valTab); angular.module('umbraco.directives.validation').directive("valTab", valTab);
@@ -116,20 +116,9 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
{ {
error: function(data){ error: function(data){
var errorMsg = 'Failed to move media'; var errorMsg = 'Failed to move media';
if (data.id !== undefined && data.parentId !== undefined) {
if (data.id === data.parentId) { if(data.parentId === data.id){
errorMsg = 'Media can\'t be moved into itself'; errorMsg = 'Media can\'t be moved into itself';
}
}
else if (data.notifications !== undefined) {
if (data.notifications.length > 0) {
if (data.notifications[0].header.length > 0) {
errorMsg = data.notifications[0].header;
}
if (data.notifications[0].message.length > 0) {
errorMsg = errorMsg + ": " + data.notifications[0].message;
}
}
} }
return { return {
@@ -1,272 +1,264 @@
/** /**
* @ngdoc service * @ngdoc service
* @name umbraco.resources.mediaTypeResource * @name umbraco.resources.mediaTypeResource
* @description Loads in data for media types * @description Loads in data for media types
**/ **/
function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter, localizationService) { function mediaTypeResource($q, $http, umbRequestHelper, umbDataFormatter) {
return { return {
getCount: function () { getCount: function () {
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.get( $http.get(
umbRequestHelper.getApiUrl( umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", "mediaTypeApiBaseUrl",
"GetCount")), "GetCount")),
'Failed to retrieve count'); 'Failed to retrieve count');
}, },
getAvailableCompositeContentTypes: function (contentTypeId, filterContentTypes, filterPropertyTypes) { getAvailableCompositeContentTypes: function (contentTypeId, filterContentTypes, filterPropertyTypes) {
if (!filterContentTypes) { if (!filterContentTypes) {
filterContentTypes = []; filterContentTypes = [];
} }
if (!filterPropertyTypes) { if (!filterPropertyTypes) {
filterPropertyTypes = []; filterPropertyTypes = [];
} }
var query = { var query = {
contentTypeId: contentTypeId, contentTypeId: contentTypeId,
filterContentTypes: filterContentTypes, filterContentTypes: filterContentTypes,
filterPropertyTypes: filterPropertyTypes filterPropertyTypes: filterPropertyTypes
}; };
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.post( $http.post(
umbRequestHelper.getApiUrl( umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", "mediaTypeApiBaseUrl",
"GetAvailableCompositeMediaTypes"), "GetAvailableCompositeMediaTypes"),
query), query),
'Failed to retrieve data for content type id ' + contentTypeId); 'Failed to retrieve data for content type id ' + contentTypeId);
}, },
/** /**
* @ngdoc method * @ngdoc method
* @name umbraco.resources.mediaTypeResource#getWhereCompositionIsUsedInContentTypes * @name umbraco.resources.mediaTypeResource#getWhereCompositionIsUsedInContentTypes
* @methodOf umbraco.resources.mediaTypeResource * @methodOf umbraco.resources.mediaTypeResource
* *
* @description * @description
* Returns a list of media types which use a specific composition with a given id * Returns a list of media types which use a specific composition with a given id
* *
* ##usage * ##usage
* <pre> * <pre>
* mediaTypeResource.getWhereCompositionIsUsedInContentTypes(1234) * mediaTypeResource.getWhereCompositionIsUsedInContentTypes(1234)
* .then(function(mediaTypeList) { * .then(function(mediaTypeList) {
* console.log(mediaTypeList); * console.log(mediaTypeList);
* }); * });
* </pre> * </pre>
* @param {Int} contentTypeId id of the composition content type to retrieve the list of the media types where it has been used * @param {Int} contentTypeId id of the composition content type to retrieve the list of the media types where it has been used
* @returns {Promise} resourcePromise object. * @returns {Promise} resourcePromise object.
* *
*/ */
getWhereCompositionIsUsedInContentTypes: function (contentTypeId) { getWhereCompositionIsUsedInContentTypes: function (contentTypeId) {
var query = { var query = {
contentTypeId: contentTypeId contentTypeId: contentTypeId
}; };
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.post( $http.post(
umbRequestHelper.getApiUrl( umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", "mediaTypeApiBaseUrl",
"GetWhereCompositionIsUsedInContentTypes"), "GetWhereCompositionIsUsedInContentTypes"),
query), query),
'Failed to retrieve data for content type id ' + contentTypeId); 'Failed to retrieve data for content type id ' + contentTypeId);
}, },
/** /**
* @ngdoc method * @ngdoc method
* @name umbraco.resources.mediaTypeResource#getAllowedTypes * @name umbraco.resources.mediaTypeResource#getAllowedTypes
* @methodOf umbraco.resources.mediaTypeResource * @methodOf umbraco.resources.mediaTypeResource
* *
* @description * @description
* Returns a list of allowed media types underneath a media item with a given ID * Returns a list of allowed media types underneath a media item with a given ID
* *
* ##usage * ##usage
* <pre> * <pre>
* mediaTypeResource.getAllowedTypes(1234) * mediaTypeResource.getAllowedTypes(1234)
* .then(function(array) { * .then(function(array) {
* $scope.type = type; * $scope.type = type;
* }); * });
* </pre> * </pre>
* @param {Int} mediaId id of the media item to retrive allowed child types for * @param {Int} mediaId id of the media item to retrive allowed child types for
* @returns {Promise} resourcePromise object. * @returns {Promise} resourcePromise object.
* *
*/ */
getAllowedTypes: function (mediaId) { getAllowedTypes: function (mediaId) {
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.get( $http.get(
umbRequestHelper.getApiUrl( umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", "mediaTypeApiBaseUrl",
"GetAllowedChildren", "GetAllowedChildren",
[{ contentId: mediaId }])), [{ contentId: mediaId }])),
'Failed to retrieve allowed types for media id ' + mediaId); 'Failed to retrieve allowed types for media id ' + mediaId);
}, },
getById: function (id) { getById: function (id) {
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.get( $http.get(
umbRequestHelper.getApiUrl( umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", "mediaTypeApiBaseUrl",
"GetById", "GetById",
[{ id: id }])), [{ id: id }])),
'Failed to retrieve content type'); 'Failed to retrieve content type');
}, },
getAll: function () { getAll: function () {
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.get( $http.get(
umbRequestHelper.getApiUrl( umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", "mediaTypeApiBaseUrl",
"GetAll")), "GetAll")),
'Failed to retrieve all content types'); 'Failed to retrieve all content types');
}, },
getScaffold: function (parentId) { getScaffold: function (parentId) {
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.get( $http.get(
umbRequestHelper.getApiUrl( umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", "mediaTypeApiBaseUrl",
"GetEmpty", { parentId: parentId })), "GetEmpty", { parentId: parentId })),
'Failed to retrieve content type scaffold'); 'Failed to retrieve content type scaffold');
}, },
deleteById: function (id) { deleteById: function (id) {
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.post( $http.post(
umbRequestHelper.getApiUrl( umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", "mediaTypeApiBaseUrl",
"DeleteById", "DeleteById",
[{ id: id }])), [{ id: id }])),
'Failed to retrieve content type'); 'Failed to retrieve content type');
}, },
deleteContainerById: function (id) { deleteContainerById: function (id) {
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.post( $http.post(
umbRequestHelper.getApiUrl( umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", "mediaTypeApiBaseUrl",
"DeleteContainer", "DeleteContainer",
[{ id: id }])), [{ id: id }])),
'Failed to delete content type contaier'); 'Failed to delete content type contaier');
}, },
/** /**
* @ngdoc method * @ngdoc method
* @name umbraco.resources.mediaTypeResource#save * @name umbraco.resources.mediaTypeResource#save
* @methodOf umbraco.resources.mediaTypeResource * @methodOf umbraco.resources.mediaTypeResource
* *
* @description * @description
* Saves or update a media type * Saves or update a media type
* *
* @param {Object} content data type object to create/update * @param {Object} content data type object to create/update
* @returns {Promise} resourcePromise object. * @returns {Promise} resourcePromise object.
* *
*/ */
save: function (contentType) { save: function (contentType) {
var saveModel = umbDataFormatter.formatContentTypePostData(contentType); var saveModel = umbDataFormatter.formatContentTypePostData(contentType);
return umbRequestHelper.resourcePromise( return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostSave"), saveModel), $http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostSave"), saveModel),
'Failed to save data for content type id ' + contentType.id); 'Failed to save data for content type id ' + contentType.id);
}, },
/** /**
* @ngdoc method * @ngdoc method
* @name umbraco.resources.mediaTypeResource#move * @name umbraco.resources.mediaTypeResource#move
* @methodOf umbraco.resources.mediaTypeResource * @methodOf umbraco.resources.mediaTypeResource
* *
* @description * @description
* Moves a node underneath a new parentId * Moves a node underneath a new parentId
* *
* ##usage * ##usage
* <pre> * <pre>
* mediaTypeResource.move({ parentId: 1244, id: 123 }) * mediaTypeResource.move({ parentId: 1244, id: 123 })
* .then(function() { * .then(function() {
* alert("node was moved"); * alert("node was moved");
* }, function(err){ * }, function(err){
* alert("node didnt move:" + err.data.Message); * alert("node didnt move:" + err.data.Message);
* }); * });
* </pre> * </pre>
* @param {Object} args arguments object * @param {Object} args arguments object
* @param {Int} args.idd the ID of the node to move * @param {Int} args.idd the ID of the node to move
* @param {Int} args.parentId the ID of the parent node to move to * @param {Int} args.parentId the ID of the parent node to move to
* @returns {Promise} resourcePromise object. * @returns {Promise} resourcePromise object.
* *
*/ */
move: function (args) { move: function (args) {
if (!args) { if (!args) {
throw "args cannot be null"; throw "args cannot be null";
} }
if (!args.parentId) { if (!args.parentId) {
throw "args.parentId cannot be null"; throw "args.parentId cannot be null";
} }
if (!args.id) { if (!args.id) {
throw "args.id cannot be null"; throw "args.id cannot be null";
} }
var promise = localizationService.localize("media_moveFailed"); return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostMove"),
return umbRequestHelper.resourcePromise( {
$http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostMove"), parentId: args.parentId,
{ id: args.id
parentId: args.parentId, }),
id: args.id 'Failed to move content');
}), },
promise);
}, copy: function (args) {
if (!args) {
copy: function (args) { throw "args cannot be null";
if (!args) { }
throw "args cannot be null"; if (!args.parentId) {
} throw "args.parentId cannot be null";
if (!args.parentId) { }
throw "args.parentId cannot be null"; if (!args.id) {
} throw "args.id cannot be null";
if (!args.id) { }
throw "args.id cannot be null";
} return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostCopy"),
var promise = localizationService.localize("media_copyFailed"); {
parentId: args.parentId,
return umbRequestHelper.resourcePromise( id: args.id
$http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", "PostCopy"), }),
{ 'Failed to copy content');
parentId: args.parentId, },
id: args.id
}), createContainer: function(parentId, name) {
promise);
}, return umbRequestHelper.resourcePromise(
$http.post(
createContainer: function(parentId, name) { umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl",
var promise = localizationService.localize("media_createFolderFailed", [parentId]); "PostCreateContainer",
{ parentId: parentId, name: encodeURIComponent(name) })),
return umbRequestHelper.resourcePromise( 'Failed to create a folder under parent id ' + parentId);
$http.post( },
umbRequestHelper.getApiUrl(
"mediaTypeApiBaseUrl", renameContainer: function (id, name) {
"PostCreateContainer",
{ parentId: parentId, name: encodeURIComponent(name) })), return umbRequestHelper.resourcePromise(
promise); $http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl",
}, "PostRenameContainer",
{ id: id, name: name })),
renameContainer: function (id, name) { "Failed to rename the folder with id " + id
);
var promise = localizationService.localize("media_renameFolderFailed", [id]);
}
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("mediaTypeApiBaseUrl", };
"PostRenameContainer", }
{ id: id, name: name })), angular.module('umbraco.resources').factory('mediaTypeResource', mediaTypeResource);
promise
);
}
};
}
angular.module('umbraco.resources').factory('mediaTypeResource', mediaTypeResource);
@@ -253,9 +253,9 @@ function contentEditingHelper(fileManager, $q, $location, $routeParams, notifica
} }
} }
// If we have a scheduled publish date change the default button to // If we have a scheduled publish or unpublish date change the default button to
// "save" and update the label to "save and schedule // "save" and update the label to "save and schedule
if(args.content.releaseDate) { if(args.content.releaseDate || args.content.removeDate) {
// if save button is alread the default don't change it just update the label // if save button is alread the default don't change it just update the label
if (buttons.defaultButton && buttons.defaultButton.letter === "A") { if (buttons.defaultButton && buttons.defaultButton.letter === "A") {
@@ -25,7 +25,6 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
var mainTreeEventHandler = null; var mainTreeEventHandler = null;
//tracks the user profile dialog //tracks the user profile dialog
var userDialog = null; var userDialog = null;
var syncTreePromise;
function setMode(mode) { function setMode(mode) {
switch (mode) { switch (mode) {
@@ -177,11 +176,6 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
//when a tree is loaded into a section, we need to put it into appState //when a tree is loaded into a section, we need to put it into appState
mainTreeEventHandler.bind("treeLoaded", function(ev, args) { mainTreeEventHandler.bind("treeLoaded", function(ev, args) {
appState.setTreeState("currentRootNode", args.tree); appState.setTreeState("currentRootNode", args.tree);
if (syncTreePromise) {
mainTreeEventHandler.syncTree(syncTreePromise.args).then(function(syncArgs) {
syncTreePromise.resolve(syncArgs);
});
}
}); });
//when a tree node is synced this event will fire, this allows us to set the currentNode //when a tree node is synced this event will fire, this allows us to set the currentNode
@@ -303,10 +297,8 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
} }
} }
//create a promise and resolve it later //couldn't sync
syncTreePromise = $q.defer(); return angularHelper.rejectedPromise();
syncTreePromise.args = args;
return syncTreePromise.promise;
}, },
/** /**
@@ -471,7 +463,7 @@ function navigationService($rootScope, $routeParams, $log, $location, $q, $timeo
//if it is not two parts long then this most likely means that it's a legacy action //if it is not two parts long then this most likely means that it's a legacy action
var js = action.metaData["jsAction"].replace("javascript:", ""); var js = action.metaData["jsAction"].replace("javascript:", "");
//there's not really a different way to achieve this except for eval //there's not really a different way to acheive this except for eval
eval(js); eval(js);
} }
else { else {
@@ -1,464 +1,455 @@
/** /**
* @ngdoc service * @ngdoc service
* @name umbraco.services.umbRequestHelper * @name umbraco.services.umbRequestHelper
* @description A helper object used for sending requests to the server * @description A helper object used for sending requests to the server
**/ **/
function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogService, notificationsService, eventsService) { function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogService, notificationsService, eventsService) {
return { return {
/** /**
* @ngdoc method * @ngdoc method
* @name umbraco.services.umbRequestHelper#convertVirtualToAbsolutePath * @name umbraco.services.umbRequestHelper#convertVirtualToAbsolutePath
* @methodOf umbraco.services.umbRequestHelper * @methodOf umbraco.services.umbRequestHelper
* @function * @function
* *
* @description * @description
* This will convert a virtual path (i.e. ~/App_Plugins/Blah/Test.html ) to an absolute path * This will convert a virtual path (i.e. ~/App_Plugins/Blah/Test.html ) to an absolute path
* *
* @param {string} a virtual path, if this is already an absolute path it will just be returned, if this is a relative path an exception will be thrown * @param {string} a virtual path, if this is already an absolute path it will just be returned, if this is a relative path an exception will be thrown
*/ */
convertVirtualToAbsolutePath: function(virtualPath) { convertVirtualToAbsolutePath: function(virtualPath) {
if (virtualPath.startsWith("/")) { if (virtualPath.startsWith("/")) {
return virtualPath; return virtualPath;
} }
if (!virtualPath.startsWith("~/")) { if (!virtualPath.startsWith("~/")) {
throw "The path " + virtualPath + " is not a virtual path"; throw "The path " + virtualPath + " is not a virtual path";
} }
if (!Umbraco.Sys.ServerVariables.application.applicationPath) { if (!Umbraco.Sys.ServerVariables.application.applicationPath) {
throw "No applicationPath defined in Umbraco.ServerVariables.application.applicationPath"; throw "No applicationPath defined in Umbraco.ServerVariables.application.applicationPath";
} }
return Umbraco.Sys.ServerVariables.application.applicationPath + virtualPath.trimStart("~/"); return Umbraco.Sys.ServerVariables.application.applicationPath + virtualPath.trimStart("~/");
}, },
/** /**
* @ngdoc method * @ngdoc method
* @name umbraco.services.umbRequestHelper#dictionaryToQueryString * @name umbraco.services.umbRequestHelper#dictionaryToQueryString
* @methodOf umbraco.services.umbRequestHelper * @methodOf umbraco.services.umbRequestHelper
* @function * @function
* *
* @description * @description
* This will turn an array of key/value pairs or a standard dictionary into a query string * This will turn an array of key/value pairs or a standard dictionary into a query string
* *
* @param {Array} queryStrings An array of key/value pairs * @param {Array} queryStrings An array of key/value pairs
*/ */
dictionaryToQueryString: function (queryStrings) { dictionaryToQueryString: function (queryStrings) {
if (angular.isArray(queryStrings)) { if (angular.isArray(queryStrings)) {
return _.map(queryStrings, function (item) { return _.map(queryStrings, function (item) {
var key = null; var key = null;
var val = null; var val = null;
for (var k in item) { for (var k in item) {
key = k; key = k;
val = item[k]; val = item[k];
break; break;
} }
if (key === null || val === null) { if (key === null || val === null) {
throw "The object in the array was not formatted as a key/value pair"; throw "The object in the array was not formatted as a key/value pair";
} }
return encodeURIComponent(key) + "=" + encodeURIComponent(val); return encodeURIComponent(key) + "=" + encodeURIComponent(val);
}).join("&"); }).join("&");
} }
else if (angular.isObject(queryStrings)) { else if (angular.isObject(queryStrings)) {
//this allows for a normal object to be passed in (ie. a dictionary) //this allows for a normal object to be passed in (ie. a dictionary)
return decodeURIComponent($.param(queryStrings)); return decodeURIComponent($.param(queryStrings));
} }
throw "The queryString parameter is not an array or object of key value pairs"; throw "The queryString parameter is not an array or object of key value pairs";
}, },
/** /**
* @ngdoc method * @ngdoc method
* @name umbraco.services.umbRequestHelper#getApiUrl * @name umbraco.services.umbRequestHelper#getApiUrl
* @methodOf umbraco.services.umbRequestHelper * @methodOf umbraco.services.umbRequestHelper
* @function * @function
* *
* @description * @description
* This will return the webapi Url for the requested key based on the servervariables collection * This will return the webapi Url for the requested key based on the servervariables collection
* *
* @param {string} apiName The webapi name that is found in the servervariables["umbracoUrls"] dictionary * @param {string} apiName The webapi name that is found in the servervariables["umbracoUrls"] dictionary
* @param {string} actionName The webapi action name * @param {string} actionName The webapi action name
* @param {object} queryStrings Can be either a string or an array containing key/value pairs * @param {object} queryStrings Can be either a string or an array containing key/value pairs
*/ */
getApiUrl: function (apiName, actionName, queryStrings) { getApiUrl: function (apiName, actionName, queryStrings) {
if (!Umbraco || !Umbraco.Sys || !Umbraco.Sys.ServerVariables || !Umbraco.Sys.ServerVariables["umbracoUrls"]) { if (!Umbraco || !Umbraco.Sys || !Umbraco.Sys.ServerVariables || !Umbraco.Sys.ServerVariables["umbracoUrls"]) {
throw "No server variables defined!"; throw "No server variables defined!";
} }
if (!Umbraco.Sys.ServerVariables["umbracoUrls"][apiName]) { if (!Umbraco.Sys.ServerVariables["umbracoUrls"][apiName]) {
throw "No url found for api name " + apiName; throw "No url found for api name " + apiName;
} }
return Umbraco.Sys.ServerVariables["umbracoUrls"][apiName] + actionName + return Umbraco.Sys.ServerVariables["umbracoUrls"][apiName] + actionName +
(!queryStrings ? "" : "?" + (angular.isString(queryStrings) ? queryStrings : this.dictionaryToQueryString(queryStrings))); (!queryStrings ? "" : "?" + (angular.isString(queryStrings) ? queryStrings : this.dictionaryToQueryString(queryStrings)));
}, },
/** /**
* @ngdoc function * @ngdoc function
* @name umbraco.services.umbRequestHelper#resourcePromise * @name umbraco.services.umbRequestHelper#resourcePromise
* @methodOf umbraco.services.umbRequestHelper * @methodOf umbraco.services.umbRequestHelper
* @function * @function
* *
* @description * @description
* This returns a promise with an underlying http call, it is a helper method to reduce * This returns a promise with an underlying http call, it is a helper method to reduce
* the amount of duplicate code needed to query http resources and automatically handle any * the amount of duplicate code needed to query http resources and automatically handle any
* Http errors. See /docs/source/using-promises-resources.md * Http errors. See /docs/source/using-promises-resources.md
* *
* @param {object} opts A mixed object which can either be a string representing the error message to be * @param {object} opts A mixed object which can either be a string representing the error message to be
* returned OR an object containing either: * returned OR an object containing either:
* { success: successCallback, errorMsg: errorMessage } * { success: successCallback, errorMsg: errorMessage }
* OR * OR
* { success: successCallback, error: errorCallback } * { success: successCallback, error: errorCallback }
* In both of the above, the successCallback must accept these parameters: data, status, headers, config * In both of the above, the successCallback must accept these parameters: data, status, headers, config
* If using the errorCallback it must accept these parameters: data, status, headers, config * If using the errorCallback it must accept these parameters: data, status, headers, config
* The success callback must return the data which will be resolved by the deferred object. * The success callback must return the data which will be resolved by the deferred object.
* The error callback must return an object containing: {errorMsg: errorMessage, data: originalData, status: status } * The error callback must return an object containing: {errorMsg: errorMessage, data: originalData, status: status }
*/ */
resourcePromise: function (httpPromise, opts) { resourcePromise: function (httpPromise, opts) {
var deferred = $q.defer(); var deferred = $q.defer();
/** The default success callback used if one is not supplied in the opts */ /** The default success callback used if one is not supplied in the opts */
function defaultSuccess(data, status, headers, config) { function defaultSuccess(data, status, headers, config) {
//when it's successful, just return the data //when it's successful, just return the data
return data; return data;
} }
/** The default error callback used if one is not supplied in the opts */ /** The default error callback used if one is not supplied in the opts */
function defaultError(data, status, headers, config) { function defaultError(data, status, headers, config) {
return {
var err = { //NOTE: the default error message here should never be used based on the above docs!
//NOTE: the default error message here should never be used based on the above docs! errorMsg: (angular.isString(opts) ? opts : 'An error occurred!'),
errorMsg: (angular.isString(opts) ? opts : 'An error occurred!'), data: data,
data: data, status: status
status: status };
}; }
// if "opts" is a promise, we set "err.errorMsg" to be that promise //create the callbacs based on whats been passed in.
if (typeof(opts) == "object" && typeof(opts.then) == "function") { var callbacks = {
err.errorMsg = opts; success: ((!opts || !opts.success) ? defaultSuccess : opts.success),
} error: ((!opts || !opts.error) ? defaultError : opts.error)
};
return err;
httpPromise.success(function (data, status, headers, config) {
}
//invoke the callback
//create the callbacs based on whats been passed in. var result = callbacks.success.apply(this, [data, status, headers, config]);
var callbacks = {
success: ((!opts || !opts.success) ? defaultSuccess : opts.success), //when it's successful, just return the data
error: ((!opts || !opts.error) ? defaultError : opts.error) deferred.resolve(result);
};
}).error(function (data, status, headers, config) {
httpPromise.success(function (data, status, headers, config) {
//invoke the callback
//invoke the callback var result = callbacks.error.apply(this, [data, status, headers, config]);
var result = callbacks.success.apply(this, [data, status, headers, config]);
//when there's a 500 (unhandled) error show a YSOD overlay if debugging is enabled.
//when it's successful, just return the data if (status >= 500 && status < 600) {
deferred.resolve(result);
//show a ysod dialog
}).error(function (data, status, headers, config) { if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) {
eventsService.emit('app.ysod',
//invoke the callback {
var result = callbacks.error.apply(this, [data, status, headers, config]); errorMsg: 'An error occured',
data: data
//when there's a 500 (unhandled) error show a YSOD overlay if debugging is enabled. });
if (status >= 500 && status < 600) { }
else {
//show a ysod dialog //show a simple error notification
if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { notificationsService.error("Server error", "Contact administrator, see log for full details.<br/><i>" + result.errorMsg + "</i>");
eventsService.emit('app.ysod', }
{
errorMsg: 'An error occured', }
data: data
}); //return an error object including the error message for UI
} deferred.reject({
else { errorMsg: result.errorMsg,
//show a simple error notification data: result.data,
notificationsService.error("Server error", "Contact administrator, see log for full details.<br/><i>" + result.errorMsg + "</i>"); status: result.status
} });
}
});
//return an error object including the error message for UI
deferred.reject({ return deferred.promise;
errorMsg: result.errorMsg,
data: result.data, },
status: result.status
}); /** Used for saving media/content specifically */
postSaveContent: function (args) {
}); if (!args.restApiUrl) {
throw "args.restApiUrl is a required argument";
return deferred.promise; }
if (!args.content) {
}, throw "args.content is a required argument";
}
/** Used for saving media/content specifically */ if (!args.action) {
postSaveContent: function (args) { throw "args.action is a required argument";
}
if (!args.restApiUrl) { if (!args.files) {
throw "args.restApiUrl is a required argument"; throw "args.files is a required argument";
} }
if (!args.content) { if (!args.dataFormatter) {
throw "args.content is a required argument"; throw "args.dataFormatter is a required argument";
} }
if (!args.action) {
throw "args.action is a required argument";
} var deferred = $q.defer();
if (!args.files) {
throw "args.files is a required argument"; //save the active tab id so we can set it when the data is returned.
} var activeTab = _.find(args.content.tabs, function (item) {
if (!args.dataFormatter) { return item.active;
throw "args.dataFormatter is a required argument"; });
} var activeTabIndex = (activeTab === undefined ? 0 : _.indexOf(args.content.tabs, activeTab));
//save the data
var deferred = $q.defer(); this.postMultiPartRequest(
args.restApiUrl,
//save the active tab id so we can set it when the data is returned. { key: "contentItem", value: args.dataFormatter(args.content, args.action) },
var activeTab = _.find(args.content.tabs, function (item) { function (data, formData) {
return item.active; //now add all of the assigned files
}); for (var f in args.files) {
var activeTabIndex = (activeTab === undefined ? 0 : _.indexOf(args.content.tabs, activeTab)); //each item has a property alias and the file object, we'll ensure that the alias is suffixed to the key
// so we know which property it belongs to on the server side
//save the data formData.append("file_" + args.files[f].alias, args.files[f].file);
this.postMultiPartRequest( }
args.restApiUrl,
{ key: "contentItem", value: args.dataFormatter(args.content, args.action) }, },
function (data, formData) { function (data, status, headers, config) {
//now add all of the assigned files //success callback
for (var f in args.files) {
//each item has a property alias and the file object, we'll ensure that the alias is suffixed to the key //reset the tabs and set the active one
// so we know which property it belongs to on the server side if(data.tabs && data.tabs.length > 0) {
formData.append("file_" + args.files[f].alias, args.files[f].file); _.each(data.tabs, function (item) {
} item.active = false;
});
}, data.tabs[activeTabIndex].active = true;
function (data, status, headers, config) { }
//success callback
//the data returned is the up-to-date data so the UI will refresh
//reset the tabs and set the active one deferred.resolve(data);
if(data.tabs && data.tabs.length > 0) { },
_.each(data.tabs, function (item) { function (data, status, headers, config) {
item.active = false; //failure callback
});
data.tabs[activeTabIndex].active = true; //when there's a 500 (unhandled) error show a YSOD overlay if debugging is enabled.
} if (status >= 500 && status < 600) {
//the data returned is the up-to-date data so the UI will refresh //This is a bit of a hack to check if the error is due to a file being uploaded that is too large,
deferred.resolve(data); // we have to just check for the existence of a string value but currently that is the best way to
}, // do this since it's very hacky/difficult to catch this on the server
function (data, status, headers, config) { if (typeof data !== "undefined" && typeof data.indexOf === "function" && data.indexOf("Maximum request length exceeded") >= 0) {
//failure callback notificationsService.error("Server error", "The uploaded file was too large, check with your site administrator to adjust the maximum size allowed");
}
//when there's a 500 (unhandled) error show a YSOD overlay if debugging is enabled. else if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) {
if (status >= 500 && status < 600) { //show a ysod dialog
eventsService.emit('app.ysod',
//This is a bit of a hack to check if the error is due to a file being uploaded that is too large, {
// we have to just check for the existence of a string value but currently that is the best way to errorMsg: 'An error occured',
// do this since it's very hacky/difficult to catch this on the server data: data
if (typeof data !== "undefined" && typeof data.indexOf === "function" && data.indexOf("Maximum request length exceeded") >= 0) { });
notificationsService.error("Server error", "The uploaded file was too large, check with your site administrator to adjust the maximum size allowed"); }
} else {
else if (Umbraco.Sys.ServerVariables["isDebuggingEnabled"] === true) { //show a simple error notification
//show a ysod dialog notificationsService.error("Server error", "Contact administrator, see log for full details.<br/><i>" + data.ExceptionMessage + "</i>");
eventsService.emit('app.ysod', }
{
errorMsg: 'An error occured', }
data: data
}); //return an error object including the error message for UI
} deferred.reject({
else { errorMsg: 'An error occurred',
//show a simple error notification data: data,
notificationsService.error("Server error", "Contact administrator, see log for full details.<br/><i>" + data.ExceptionMessage + "</i>"); status: status
} });
}
});
//return an error object including the error message for UI
deferred.reject({ return deferred.promise;
errorMsg: 'An error occurred', },
data: data,
status: status /** Posts a multi-part mime request to the server */
}); postMultiPartRequest: function (url, jsonData, transformCallback, successCallback, failureCallback) {
//validate input, jsonData can be an array of key/value pairs or just one key/value pair.
}); if (!jsonData) { throw "jsonData cannot be null"; }
return deferred.promise; if (angular.isArray(jsonData)) {
}, _.each(jsonData, function (item) {
if (!item.key || !item.value) { throw "jsonData array item must have both a key and a value property"; }
/** Posts a multi-part mime request to the server */ });
postMultiPartRequest: function (url, jsonData, transformCallback, successCallback, failureCallback) { }
else if (!jsonData.key || !jsonData.value) { throw "jsonData object must have both a key and a value property"; }
//validate input, jsonData can be an array of key/value pairs or just one key/value pair.
if (!jsonData) { throw "jsonData cannot be null"; }
$http({
if (angular.isArray(jsonData)) { method: 'POST',
_.each(jsonData, function (item) { url: url,
if (!item.key || !item.value) { throw "jsonData array item must have both a key and a value property"; } //IMPORTANT!!! You might think this should be set to 'multipart/form-data' but this is not true because when we are sending up files
}); // the request needs to include a 'boundary' parameter which identifies the boundary name between parts in this multi-part request
} // and setting the Content-type manually will not set this boundary parameter. For whatever reason, setting the Content-type to 'false'
else if (!jsonData.key || !jsonData.value) { throw "jsonData object must have both a key and a value property"; } // will force the request to automatically populate the headers properly including the boundary parameter.
headers: { 'Content-Type': false },
transformRequest: function (data) {
$http({ var formData = new FormData();
method: 'POST', //add the json data
url: url, if (angular.isArray(data)) {
//IMPORTANT!!! You might think this should be set to 'multipart/form-data' but this is not true because when we are sending up files _.each(data, function (item) {
// the request needs to include a 'boundary' parameter which identifies the boundary name between parts in this multi-part request formData.append(item.key, !angular.isString(item.value) ? angular.toJson(item.value) : item.value);
// and setting the Content-type manually will not set this boundary parameter. For whatever reason, setting the Content-type to 'false' });
// will force the request to automatically populate the headers properly including the boundary parameter. }
headers: { 'Content-Type': false }, else {
transformRequest: function (data) { formData.append(data.key, !angular.isString(data.value) ? angular.toJson(data.value) : data.value);
var formData = new FormData(); }
//add the json data
if (angular.isArray(data)) { //call the callback
_.each(data, function (item) { if (transformCallback) {
formData.append(item.key, !angular.isString(item.value) ? angular.toJson(item.value) : item.value); transformCallback.apply(this, [data, formData]);
}); }
}
else { return formData;
formData.append(data.key, !angular.isString(data.value) ? angular.toJson(data.value) : data.value); },
} data: jsonData
}).
//call the callback success(function (data, status, headers, config) {
if (transformCallback) { if (successCallback) {
transformCallback.apply(this, [data, formData]); successCallback.apply(this, [data, status, headers, config]);
} }
}).
return formData; error(function (data, status, headers, config) {
}, if (failureCallback) {
data: jsonData failureCallback.apply(this, [data, status, headers, config]);
}). }
success(function (data, status, headers, config) { });
if (successCallback) { },
successCallback.apply(this, [data, status, headers, config]);
} /**
}). * Downloads a file to the client using AJAX/XHR
error(function (data, status, headers, config) { * Based on an implementation here: web.student.tuwien.ac.at/~e0427417/jsdownload.html
if (failureCallback) { * See https://stackoverflow.com/a/24129082/694494
failureCallback.apply(this, [data, status, headers, config]); */
} downloadFile : function (httpPath) {
});
}, var deferred = $q.defer();
/** // Use an arraybuffer
* Downloads a file to the client using AJAX/XHR $http.get(httpPath, { responseType: 'arraybuffer' })
* Based on an implementation here: web.student.tuwien.ac.at/~e0427417/jsdownload.html .success(function (data, status, headers) {
* See https://stackoverflow.com/a/24129082/694494
*/ var octetStreamMime = 'application/octet-stream';
downloadFile : function (httpPath) { var success = false;
var deferred = $q.defer(); // Get the headers
headers = headers();
// Use an arraybuffer
$http.get(httpPath, { responseType: 'arraybuffer' }) // Get the filename from the x-filename header or default to "download.bin"
.success(function (data, status, headers) { var filename = headers['x-filename'] || 'download.bin';
var octetStreamMime = 'application/octet-stream'; // Determine the content type from the header or default to "application/octet-stream"
var success = false; var contentType = headers['content-type'] || octetStreamMime;
// Get the headers try {
headers = headers(); // Try using msSaveBlob if supported
console.log("Trying saveBlob method ...");
// Get the filename from the x-filename header or default to "download.bin" var blob = new Blob([data], { type: contentType });
var filename = headers['x-filename'] || 'download.bin'; if (navigator.msSaveBlob)
navigator.msSaveBlob(blob, filename);
// Determine the content type from the header or default to "application/octet-stream" else {
var contentType = headers['content-type'] || octetStreamMime; // Try using other saveBlob implementations, if available
var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob;
try { if (saveBlob === undefined) throw "Not supported";
// Try using msSaveBlob if supported saveBlob(blob, filename);
console.log("Trying saveBlob method ..."); }
var blob = new Blob([data], { type: contentType }); console.log("saveBlob succeeded");
if (navigator.msSaveBlob) success = true;
navigator.msSaveBlob(blob, filename); } catch (ex) {
else { console.log("saveBlob method failed with the following exception:");
// Try using other saveBlob implementations, if available console.log(ex);
var saveBlob = navigator.webkitSaveBlob || navigator.mozSaveBlob || navigator.saveBlob; }
if (saveBlob === undefined) throw "Not supported";
saveBlob(blob, filename); if (!success) {
} // Get the blob url creator
console.log("saveBlob succeeded"); var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL;
success = true; if (urlCreator) {
} catch (ex) { // Try to use a download link
console.log("saveBlob method failed with the following exception:"); var link = document.createElement('a');
console.log(ex); if ('download' in link) {
} // Try to simulate a click
try {
if (!success) { // Prepare a blob URL
// Get the blob url creator console.log("Trying download link method with simulated click ...");
var urlCreator = window.URL || window.webkitURL || window.mozURL || window.msURL; var blob = new Blob([data], { type: contentType });
if (urlCreator) { var url = urlCreator.createObjectURL(blob);
// Try to use a download link link.setAttribute('href', url);
var link = document.createElement('a');
if ('download' in link) { // Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
// Try to simulate a click link.setAttribute("download", filename);
try {
// Prepare a blob URL // Simulate clicking the download link
console.log("Trying download link method with simulated click ..."); var event = document.createEvent('MouseEvents');
var blob = new Blob([data], { type: contentType }); event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null);
var url = urlCreator.createObjectURL(blob); link.dispatchEvent(event);
link.setAttribute('href', url); console.log("Download link method with simulated click succeeded");
success = true;
// Set the download attribute (Supported in Chrome 14+ / Firefox 20+)
link.setAttribute("download", filename); } catch (ex) {
console.log("Download link method with simulated click failed with the following exception:");
// Simulate clicking the download link console.log(ex);
var event = document.createEvent('MouseEvents'); }
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0, false, false, false, false, 0, null); }
link.dispatchEvent(event);
console.log("Download link method with simulated click succeeded"); if (!success) {
success = true; // Fallback to window.location method
try {
} catch (ex) { // Prepare a blob URL
console.log("Download link method with simulated click failed with the following exception:"); // Use application/octet-stream when using window.location to force download
console.log(ex); console.log("Trying download link method with window.location ...");
} var blob = new Blob([data], { type: octetStreamMime });
} var url = urlCreator.createObjectURL(blob);
window.location = url;
if (!success) { console.log("Download link method with window.location succeeded");
// Fallback to window.location method success = true;
try { } catch (ex) {
// Prepare a blob URL console.log("Download link method with window.location failed with the following exception:");
// Use application/octet-stream when using window.location to force download console.log(ex);
console.log("Trying download link method with window.location ..."); }
var blob = new Blob([data], { type: octetStreamMime }); }
var url = urlCreator.createObjectURL(blob);
window.location = url; }
console.log("Download link method with window.location succeeded"); }
success = true;
} catch (ex) { if (!success) {
console.log("Download link method with window.location failed with the following exception:"); // Fallback to window.open method
console.log(ex); console.log("No methods worked for saving the arraybuffer, using last resort window.open");
} window.open(httpPath, '_blank', '');
} }
} deferred.resolve();
} })
.error(function (data, status) {
if (!success) { console.log("Request failed with status: " + status);
// Fallback to window.open method
console.log("No methods worked for saving the arraybuffer, using last resort window.open"); deferred.reject({
window.open(httpPath, '_blank', ''); errorMsg: "An error occurred downloading the file",
} data: data,
status: status
deferred.resolve(); });
}) });
.error(function (data, status) {
console.log("Request failed with status: " + status); return deferred.promise;
}
deferred.reject({ };
errorMsg: "An error occurred downloading the file", }
data: data, angular.module('umbraco.services').factory('umbRequestHelper', umbRequestHelper);
status: status
});
});
return deferred.promise;
}
};
}
angular.module('umbraco.services').factory('umbRequestHelper', umbRequestHelper);
@@ -11,7 +11,7 @@
*/ */
function NavigationController($scope, $rootScope, $location, $log, $routeParams, $timeout, appState, navigationService, keyboardService, dialogService, historyService, eventsService, sectionResource, angularHelper) { function NavigationController($scope, $rootScope, $location, $log, $routeParams, $timeout, appState, navigationService, keyboardService, dialogService, historyService, eventsService, sectionResource, angularHelper) {
//TODO: Need to think about this and an nicer way to achieve what this is doing. //TODO: Need to think about this and an nicer way to acheive what this is doing.
//the tree event handler i used to subscribe to the main tree click events //the tree event handler i used to subscribe to the main tree click events
$scope.treeEventHandler = $({}); $scope.treeEventHandler = $({});
navigationService.setupTreeEvents($scope.treeEventHandler); navigationService.setupTreeEvents($scope.treeEventHandler);
@@ -40,7 +40,7 @@ function NavigationController($scope, $rootScope, $location, $log, $routeParams,
navigationService.showSearch(); navigationService.showSearch();
}); });
//trigger dialogs with a hotkey: //trigger dialods with a hotkey:
keyboardService.bind("esc", function () { keyboardService.bind("esc", function () {
eventsService.emit("app.closeDialogs"); eventsService.emit("app.closeDialogs");
}); });
@@ -112,7 +112,7 @@ function NavigationController($scope, $rootScope, $location, $log, $routeParams,
$scope.authenticated = false; $scope.authenticated = false;
})); }));
//when the application is ready and the user is authorized, setup the data //when the application is ready and the user is authorized setup the data
evts.push(eventsService.on("app.ready", function(evt, data) { evts.push(eventsService.on("app.ready", function(evt, data) {
$scope.authenticated = true; $scope.authenticated = true;
})); }));
@@ -32,7 +32,6 @@
ng-pattern="passwordPattern" ng-pattern="passwordPattern"
autocorrect="off" autocorrect="off"
autocapitalize="off" autocapitalize="off"
autocomplete="off"
required required
ng-model="installer.current.model.password" id="password" /> ng-model="installer.current.model.password" id="password" />
<small class="inline-help">At least {{installer.current.model.minCharLength}} characters long</small> <small class="inline-help">At least {{installer.current.model.minCharLength}} characters long</small>
+186 -187
View File
@@ -1,187 +1,186 @@
// Core variables and mixins // Core variables and mixins
@import "fonts.less"; // Loading fonts @import "fonts.less"; // Loading fonts
@import "variables.less"; // Modify this for custom colors, font-sizes, etc @import "variables.less"; // Modify this for custom colors, font-sizes, etc
@import "mixins.less"; @import "mixins.less";
// CSS Reset // CSS Reset
@import "../../lib/bootstrap/less/reset.less"; @import "../../lib/bootstrap/less/reset.less";
// Grid system and page structure // Grid system and page structure
@import "../../lib/bootstrap/less/scaffolding.less"; @import "../../lib/bootstrap/less/scaffolding.less";
@import "../../lib/bootstrap/less/grid.less"; @import "../../lib/bootstrap/less/grid.less";
@import "../../lib/bootstrap/less/layouts.less"; @import "../../lib/bootstrap/less/layouts.less";
// Base CSS // Base CSS
@import "../../lib/bootstrap/less/type.less"; @import "../../lib/bootstrap/less/type.less";
@import "../../lib/bootstrap/less/code.less"; @import "../../lib/bootstrap/less/code.less";
@import "tables.less"; @import "tables.less";
// Components: common // Components: common
@import "../../lib/bootstrap/less/dropdowns.less"; @import "../../lib/bootstrap/less/dropdowns.less";
@import "../../lib/bootstrap/less/wells.less"; @import "../../lib/bootstrap/less/wells.less";
@import "../../lib/bootstrap/less/component-animations.less"; @import "../../lib/bootstrap/less/component-animations.less";
@import "../../lib/bootstrap/less/close.less"; @import "../../lib/bootstrap/less/close.less";
// Components: Buttons & Alerts // Components: Buttons & Alerts
@import "../../lib/bootstrap/less/button-groups.less"; @import "../../lib/bootstrap/less/button-groups.less";
@import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less @import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less
// Components: Nav // Components: Nav
@import "navs.less"; @import "navs.less";
@import "../../lib/bootstrap/less/navbar.less"; @import "../../lib/bootstrap/less/navbar.less";
@import "../../lib/bootstrap/less/breadcrumbs.less"; @import "../../lib/bootstrap/less/breadcrumbs.less";
@import "../../lib/bootstrap/less/pagination.less"; @import "../../lib/bootstrap/less/pagination.less";
@import "../../lib/bootstrap/less/pager.less"; @import "../../lib/bootstrap/less/pager.less";
// Components: Popovers // Components: Popovers
@import "../../lib/bootstrap/less/modals.less"; @import "../../lib/bootstrap/less/modals.less";
@import "../../lib/bootstrap/less/tooltip.less"; @import "../../lib/bootstrap/less/tooltip.less";
@import "../../lib/bootstrap/less/popovers.less"; @import "../../lib/bootstrap/less/popovers.less";
@import "tipmenu.less"; @import "tipmenu.less";
// Components: Misc // Components: Misc
@import "../../lib/bootstrap/less/thumbnails.less"; @import "../../lib/bootstrap/less/thumbnails.less";
@import "../../lib/bootstrap/less/media.less"; @import "../../lib/bootstrap/less/media.less";
@import "../../lib/bootstrap/less/labels-badges.less"; @import "../../lib/bootstrap/less/labels-badges.less";
@import "../../lib/bootstrap/less/progress-bars.less"; @import "../../lib/bootstrap/less/progress-bars.less";
@import "../../lib/bootstrap/less/accordion.less"; @import "../../lib/bootstrap/less/accordion.less";
@import "../../lib/bootstrap/less/carousel.less"; @import "../../lib/bootstrap/less/carousel.less";
@import "../../lib/bootstrap/less/hero-unit.less"; @import "../../lib/bootstrap/less/hero-unit.less";
// Utility classes // Utility classes
@import "../../lib/bootstrap/less/utilities.less"; // Has to be last to override when necessary @import "../../lib/bootstrap/less/utilities.less"; // Has to be last to override when necessary
// Application wide styles (refactor is WIP) // Application wide styles (refactor is WIP)
@import "application/grid.less"; @import "application/grid.less";
@import "application/shadows.less"; @import "application/shadows.less";
@import "application/animations.less"; @import "application/animations.less";
// Utilities // Utilities
@import "utilities/_font-weight.less"; @import "utilities/_font-weight.less";
// Belle styles // Belle styles
@import "buttons.less"; @import "buttons.less";
@import "forms.less"; @import "forms.less";
@import "legacydialog.less"; @import "modals.less";
@import "modals.less"; @import "panel.less";
@import "panel.less"; @import "sections.less";
@import "sections.less"; @import "helveticons.less";
@import "helveticons.less"; @import "main.less";
@import "main.less"; @import "tree.less";
@import "tree.less"; @import "listview.less";
@import "listview.less"; @import "gridview.less";
@import "gridview.less"; @import "footer.less";
@import "footer.less"; @import "dragdrop.less";
@import "dragdrop.less"; @import "dashboards.less";
@import "dashboards.less";
@import "forms/umb-validation-label.less";
@import "forms/umb-validation-label.less";
// Umbraco Components
// Umbraco Components @import "components/application/umb-tour.less";
@import "components/application/umb-tour.less"; @import "components/application/umb-backdrop.less";
@import "components/application/umb-backdrop.less"; @import "components/application/umb-drawer.less";
@import "components/application/umb-drawer.less";
@import "components/editor.less";
@import "components/editor.less"; @import "components/overlays.less";
@import "components/overlays.less"; @import "components/card.less";
@import "components/card.less"; @import "components/umb-sub-views.less";
@import "components/umb-sub-views.less"; @import "components/umb-editor-navigation.less";
@import "components/umb-editor-navigation.less"; @import "components/umb-editor-sub-views.less";
@import "components/umb-editor-sub-views.less"; @import "components/editor/subheader/umb-editor-sub-header.less";
@import "components/editor/subheader/umb-editor-sub-header.less"; @import "components/umb-grid-selector.less";
@import "components/umb-grid-selector.less"; @import "components/umb-child-selector.less";
@import "components/umb-child-selector.less"; @import "components/umb-group-builder.less";
@import "components/umb-group-builder.less"; @import "components/umb-list-view-settings.less";
@import "components/umb-list-view-settings.less"; @import "components/umb-table.less";
@import "components/umb-table.less"; @import "components/umb-confirm-action.less";
@import "components/umb-confirm-action.less"; @import "components/umb-keyboard-shortcuts-overview.less";
@import "components/umb-keyboard-shortcuts-overview.less"; @import "components/umb-checkbox-list.less";
@import "components/umb-checkbox-list.less"; @import "components/umb-locked-field.less";
@import "components/umb-locked-field.less"; @import "components/umb-tabs.less";
@import "components/umb-tabs.less"; @import "components/umb-load-indicator.less";
@import "components/umb-load-indicator.less"; @import "components/umb-breadcrumbs.less";
@import "components/umb-breadcrumbs.less"; @import "components/umb-media-grid.less";
@import "components/umb-media-grid.less"; @import "components/umb-folder-grid.less";
@import "components/umb-folder-grid.less"; @import "components/umb-content-grid.less";
@import "components/umb-content-grid.less"; @import "components/umb-layout-selector.less";
@import "components/umb-layout-selector.less"; @import "components/tooltip/umb-tooltip.less";
@import "components/tooltip/umb-tooltip.less"; @import "components/tooltip/umb-tooltip-list.less";
@import "components/tooltip/umb-tooltip-list.less"; @import "components/overlays/umb-overlay-backdrop.less";
@import "components/overlays/umb-overlay-backdrop.less"; @import "components/umb-grid.less";
@import "components/umb-grid.less"; @import "components/umb-empty-state.less";
@import "components/umb-empty-state.less"; @import "components/umb-property-editor.less";
@import "components/umb-property-editor.less"; @import "components/umb-color-swatches.less";
@import "components/umb-color-swatches.less"; @import "components/check-circle.less";
@import "components/check-circle.less"; @import "components/umb-iconpicker.less";
@import "components/umb-iconpicker.less"; @import "components/umb-insert-code-box.less";
@import "components/umb-insert-code-box.less"; @import "components/umb-packages.less";
@import "components/umb-packages.less"; @import "components/umb-package-local-install.less";
@import "components/umb-package-local-install.less"; @import "components/umb-lightbox.less";
@import "components/umb-lightbox.less"; @import "components/umb-avatar.less";
@import "components/umb-avatar.less"; @import "components/umb-progress-bar.less";
@import "components/umb-progress-bar.less"; @import "components/umb-querybuilder.less";
@import "components/umb-querybuilder.less"; @import "components/umb-pagination.less";
@import "components/umb-pagination.less"; @import "components/umb-mini-list-view.less";
@import "components/umb-mini-list-view.less"; @import "components/umb-multiple-textbox.less";
@import "components/umb-multiple-textbox.less"; @import "components/umb-badge.less";
@import "components/umb-badge.less"; @import "components/umb-nested-content.less";
@import "components/umb-nested-content.less"; @import "components/umb-checkmark.less";
@import "components/umb-checkmark.less"; @import "components/umb-list.less";
@import "components/umb-list.less"; @import "components/umb-box.less";
@import "components/umb-box.less"; @import "components/umb-number-badge.less";
@import "components/umb-number-badge.less"; @import "components/umb-progress-circle.less";
@import "components/umb-progress-circle.less";
@import "components/buttons/umb-button.less";
@import "components/buttons/umb-button.less"; @import "components/buttons/umb-button-group.less";
@import "components/buttons/umb-button-group.less"; @import "components/buttons/umb-era-button.less";
@import "components/buttons/umb-era-button.less"; @import "components/buttons/umb-toggle.less";
@import "components/buttons/umb-toggle.less";
@import "components/notifications/umb-notifications.less";
@import "components/notifications/umb-notifications.less"; @import "components/umb-file-dropzone.less";
@import "components/umb-file-dropzone.less"; @import "components/umb-node-preview.less";
@import "components/umb-node-preview.less"; @import "components/umb-mini-editor.less";
@import "components/umb-mini-editor.less";
@import "components/users/umb-user-cards.less";
@import "components/users/umb-user-cards.less"; @import "components/users/umb-user-details.less";
@import "components/users/umb-user-details.less"; @import "components/users/umb-user-group-picker-list.less";
@import "components/users/umb-user-group-picker-list.less"; @import "components/users/umb-user-group-preview.less";
@import "components/users/umb-user-group-preview.less"; @import "components/users/umb-user-preview.less";
@import "components/users/umb-user-preview.less"; @import "components/users/umb-user-picker-list.less";
@import "components/users/umb-user-picker-list.less"; @import "components/users/umb-permission.less";
@import "components/users/umb-permission.less";
// Utilities
// Utilities @import "utilities/layout/_display.less";
@import "utilities/layout/_display.less"; @import "utilities/typography/_text-decoration.less";
@import "utilities/typography/_text-decoration.less"; @import "utilities/typography/_white-space.less";
@import "utilities/typography/_white-space.less"; @import "utilities/_flexbox.less";
@import "utilities/_flexbox.less"; @import "utilities/_spacing.less";
@import "utilities/_spacing.less"; @import "utilities/_text-align.less";
@import "utilities/_text-align.less"; @import "utilities/_width.less";
@import "utilities/_width.less";
//page specific styles
//page specific styles @import "pages/document-type-editor.less";
@import "pages/document-type-editor.less"; @import "pages/login.less";
@import "pages/login.less"; @import "pages/welcome-dashboard.less";
@import "pages/welcome-dashboard.less";
//used for property editors
//used for property editors @import "property-editors.less";
@import "property-editors.less";
//used for prevalue editors
//used for prevalue editors @import "components/prevalues/multivalues.less";
@import "components/prevalues/multivalues.less";
@import "typeahead.less";
@import "typeahead.less"; @import "hacks.less";
@import "hacks.less";
@import "healthcheck.less";
@import "healthcheck.less"; @import "getstarted.less";
@import "getstarted.less";
// cleanup properties.less when it is done
// cleanup properties.less when it is done @import "properties.less";
@import "properties.less";
@@ -110,4 +110,4 @@
.umb-era-button i { .umb-era-button i {
margin-right: 5px; margin-right: 5px;
} }
@@ -102,13 +102,11 @@
} }
.umb-card-grid li.-four-in-row { .umb-card-grid li.-four-in-row {
flex: 0 0 25%; flex: 0 0 25%;
max-width: 25%;
} }
.umb-card-grid li.-three-in-row { .umb-card-grid li.-three-in-row {
flex: 0 0 33.33%; flex: 0 0 33.33%;
max-width:33.33%;
} }
.umb-card-grid .umb-card-grid-item { .umb-card-grid .umb-card-grid-item {
@@ -174,7 +174,6 @@
box-sizing: border-box; box-sizing: border-box;
background: @gray-10; background: @gray-10;
border-bottom: 1px solid @purple-l3; border-bottom: 1px solid @purple-l3;
pointer-events: none;
} }
.umb-overlay__item-details-title-wrapper { .umb-overlay__item-details-title-wrapper {
@@ -5,10 +5,6 @@
.umb-overlay & { .umb-overlay & {
width: 500px; width: 500px;
} }
p{
margin: 7px 0;
}
} }
.umb-prevalues-multivalues__left { .umb-prevalues-multivalues__left {
@@ -3,7 +3,7 @@
flex-flow: row wrap; flex-flow: row wrap;
.umb-color-box { .umb-color-box {
border: 1px solid rgba(0,0,0,0.15); border: 1px solid @gray-8;
color: @white; color: @white;
cursor: pointer; cursor: pointer;
padding: 1px; padding: 1px;
@@ -42,11 +42,10 @@
&.with-labels { &.with-labels {
.umb-color-box { .umb-color-box {
width: 130px; width: 120px;
height: auto; height: 100%;
display: flex; display: flex;
flex-flow: row wrap; flex-flow: row wrap;
border: 1px solid @gray-8;
.umb-color-box-inner { .umb-color-box-inner {
display: flex; display: flex;
@@ -54,21 +53,15 @@
flex: 0 0 100%; flex: 0 0 100%;
max-width: 100%; max-width: 100%;
min-height: 80px; min-height: 80px;
padding: 0; padding-top: 10px;
.check_circle {
margin: 15px auto;
}
.umb-color-box__label { .umb-color-box__label {
background: @white; background: @white;
font-size: 14px; font-size: 14px;
display: flex; display: flex;
flex-flow: column wrap; flex-flow: column wrap;
flex: 1 0 100%; flex: 0 0 100%;
justify-content: flex-end;
padding: 1px 5px; padding: 1px 5px;
min-height: 45px;
max-width: 100%; max-width: 100%;
margin-top: auto; margin-top: auto;
margin-bottom: -3px; margin-bottom: -3px;
@@ -1,17 +1,4 @@
.umb-multiple-textbox{ .umb-multiple-textbox .textbox-wrapper {
&__confirm{
position: relative;
&-action{
margin: 0;
padding: 2px;
background: transparent;
border: 0 none;
}
}
}
.umb-multiple-textbox .textbox-wrapper {
align-items: center; align-items: center;
margin-bottom: 15px; margin-bottom: 15px;
} }
@@ -20,7 +7,7 @@
margin-bottom: 0; margin-bottom: 0;
} }
.umb-multiple-textbox .textbox-wrapper i:not(.icon-delete, .icon-check) { .umb-multiple-textbox .textbox-wrapper i {
margin-right: 5px; margin-right: 5px;
} }
@@ -209,6 +209,26 @@
width: 99%; width: 99%;
} }
.usky-grid.umb-nested-content__node-type-picker {
.cell-tools-menu {
position: relative;
transform: translate(-50%, -25%);
}
.elements li {
&:hover {
i {
color: @white !important;
}
}
i {
// make sure the item icons shown are in the correct color according to their doc type icon instead of the grid editor item color
color: unset;
}
}
}
// this resolves the layout issue introduced in nested content in 7.12 with the addition of the input for link anchors // this resolves the layout issue introduced in nested content in 7.12 with the addition of the input for link anchors
// the attribute selector ensures the change only applies to the linkpicker overlay // the attribute selector ensures the change only applies to the linkpicker overlay
.form-horizontal .umb-nested-content--narrow [ng-controller*="Umbraco.Overlays.LinkPickerController"] .controls-row { .form-horizontal .umb-nested-content--narrow [ng-controller*="Umbraco.Overlays.LinkPickerController"] .controls-row {
@@ -594,7 +594,7 @@ div.help {
text-align: center; text-align: center;
text-shadow: 0 1px 0 @white; text-shadow: 0 1px 0 @white;
background-color: @gray-10; background-color: @gray-10;
border: 1px solid @purple-l3; border: 1px solid @gray-8;
} }
.add-on, .add-on,
.btn, .btn,
@@ -1,7 +1,5 @@
.umb-validation-label { .umb-validation-label {
position: absolute; position: relative;
top: 27px;
width: 200px;
padding: 1px 5px; padding: 1px 5px;
background: @red; background: @red;
color: @white; color: @white;
@@ -202,7 +202,7 @@ pre {
//font-size: @baseFontSize - 1; // 14px to 13px //font-size: @baseFontSize - 1; // 14px to 13px
color: @gray-2; color: @gray-2;
line-height: @baseLineHeight; line-height: @baseLineHeight;
white-space: pre-wrap; // 1 white-space: pre-line; // 1
overflow-x: auto; // 1 overflow-x: auto; // 1
background-color: @gray-10; background-color: @gray-10;
border: 1px solid @gray-8; border: 1px solid @gray-8;
@@ -222,4 +222,4 @@ pre {
background-color: transparent; background-color: transparent;
border: 0; border: 0;
} }
} }
@@ -236,5 +236,4 @@
.umb-healthcheck-group__details-status-action-description { .umb-healthcheck-group__details-status-action-description {
margin-top: 5px; margin-top: 5px;
font-size: 12px; font-size: 12px;
padding-left: 165px;
} }
@@ -1,58 +0,0 @@
.umb-dialog .propertyItemheader {
width: 140px !Important;
}
.umb-dialog .diffDropdown {
width: 400px;
}
.umb-dialog .diffPanel {
height: 400px;
}
.umb-dialog .diff {
margin-top: 10px;
height: 100%;
overflow: auto;
border-top: 1px solid #ccc;
border-top: 1px solid #ccc;
padding: 5px;
}
.umb-dialog .diff table {
width: 95%;
max-width: 95%;
margin: 0 3px;
}
.umb-dialog .diff table th {
padding: 5px;
width: 25%;
border-bottom: 1px solid #ccc;
}
.umb-dialog .diff table td {
border-bottom: 1px solid #ccc;
padding: 3px;
}
.umb-dialog .diff del {
background: rgb(255, 230, 230) none repeat scroll 0%;
-moz-background-clip: -moz-initial;
-moz-background-origin: -moz-initial;
-moz-background-inline-policy: -moz-initial;
}
.umb-dialog .diff ins {
background: rgb(230, 255, 230) none repeat scroll 0%;
-moz-background-clip: -moz-initial;
-moz-background-origin: -moz-initial;
-moz-background-inline-policy: -moz-initial;
}
.umb-dialog .diff .diffnotice {
text-align: center;
margin-bottom: 10px;
}
+5 -16
View File
@@ -23,7 +23,7 @@
margin-top: 0px; margin-top: 0px;
margin-bottom: 15px; margin-bottom: 15px;
font-size: 14px; font-size: 14px;
color: @gray-7; color: @gray-7;
} }
h5{ h5{
@@ -134,7 +134,7 @@ h5.-black {
padding-bottom: 0; padding-bottom: 0;
} }
.block-form .umb-control-group label .help-block, .block-form .umb-control-group label .help-block,
.block-form .umb-control-group label small { .block-form .umb-control-group label small {
font-size: 13px; font-size: 13px;
padding-top: 2px; padding-top: 2px;
@@ -243,9 +243,9 @@ label:not([for]) {
} }
.umb-version { .umb-version {
color: @gray-7; color: @gray-7;
position: absolute; position: absolute;
bottom: 5px; bottom: 5px;
right: 20px; right: 20px;
} }
@@ -653,14 +653,3 @@ input[type=checkbox]:checked + .input-label--small {
.bootstrap-datetimepicker-widget td span { .bootstrap-datetimepicker-widget td span {
border-radius: 0 !important; border-radius: 0 !important;
} }
.visuallyhidden{
position: absolute !important;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px, 1px, 1px, 1px);
padding:0 !important;
border:0 !important;
height: 1px !important;
width: 1px !important;
overflow: hidden;
}
+254 -197
View File
@@ -1,197 +1,254 @@
/* Modals /* Modals
// -------------------------*/ // -------------------------*/
/* Modalcolumn is used for menu panels */ /* Modalcolumn is used for menu panels */
.umb-modalcolumn { .umb-modalcolumn {
background: @white; background: @white;
} }
.umb-modalcolumn-header { .umb-modalcolumn-header {
background: @gray-10; background: @gray-10;
border-bottom: 1px solid @purple-l3; border-bottom: 1px solid @purple-l3;
height: 94px; height: 94px;
padding: 5px 20px 0px 20px; padding: 5px 20px 0px 20px;
white-space: nowrap white-space: nowrap
} }
.umb-modalcolumn-header h1{ .umb-modalcolumn-header h1{
margin: 0; margin: 0;
white-space: nowrap; white-space: nowrap;
font-size: @fontSizeLarge; font-size: @fontSizeLarge;
font-weight: 400; font-weight: 400;
padding-top: 10px !important; padding-top: 10px !important;
} }
.umb-modalcolumn-body { .umb-modalcolumn-body {
padding: 0px; padding: 0px;
background: @white; background: @white;
top: 100px; top: 100px;
position: absolute; position: absolute;
left: 0px; left: 0px;
right: 0px; right: 0px;
bottom: 0px; bottom: 0px;
overflow: auto; overflow: auto;
} }
.no-padding .umb-modalcolumn-body { .no-padding .umb-modalcolumn-body {
padding: 0px padding: 0px
} }
.umb-modalcolumn .umb-modalcolumn-header .btn { .umb-modalcolumn .umb-modalcolumn-header .btn {
position: absolute; position: absolute;
top: 13px; top: 13px;
right: 15px right: 15px
} }
.umb-modalcolumn iframe.auto-expand, .umb-modal iframe.auto-expand { .umb-modalcolumn iframe.auto-expand, .umb-modal iframe.auto-expand {
border: none; border: none;
padding: 0px; padding: 0px;
margin: 0px; margin: 0px;
top: 0px; top: 0px;
bottom: 0px; bottom: 0px;
left: 0px; left: 0px;
right: 0px; right: 0px;
position: absolute;; position: absolute;;
} }
/* re-align loader */ /* re-align loader */
.umb-modal .umb-loader-wrapper, .umb-modalcolumn .umb-loader-wrapper, .umb-dialog .umb-loader-wrapper{ .umb-modal .umb-loader-wrapper, .umb-modalcolumn .umb-loader-wrapper, .umb-dialog .umb-loader-wrapper{
position:relative; position:relative;
margin: 20px -20px; margin: 20px -20px;
} }
.umb-modal-left .umb-panel-header .umb-headline, .umb-modal-left .umb-panel-header h1 { .umb-modal-left .umb-panel-header .umb-headline, .umb-modal-left .umb-panel-header h1 {
width: auto; width: auto;
padding-left: 0; padding-left: 0;
} }
/* umb.dialog is used for the dialogs on the conent tree*/ /* umb.dialog is used for the dialogs on the conent tree*/
.umb-dialog { .umb-dialog {
outline: none; outline: none;
top: 0px; top: 0px;
left: 0px; left: 0px;
right: 0px; right: 0px;
bottom: 0px; bottom: 0px;
position: absolute; position: absolute;
padding: 0px; padding: 0px;
} }
.umb-dialog .umb-btn-toolbar .umb-control-group{ .umb-dialog .umb-btn-toolbar .umb-control-group{
border: none; border: none;
padding: none; padding: none;
} }
.umb-dialog-body{ .umb-dialog-body{
position: absolute; position: absolute;
overflow:auto; overflow:auto;
top: 0px; top: 0px;
left: 0px; left: 0px;
right: 0px; right: 0px;
bottom: 52px; bottom: 52px;
} }
.umb-dialog-body .umb-pane{margin-top: 15px;} .umb-dialog-body .umb-pane{margin-top: 15px;}
.umb-dialog-footer{ .umb-dialog-footer{
position: absolute; position: absolute;
overflow:auto; overflow:auto;
text-align: right; text-align: right;
height: 32px; height: 32px;
left: 0px; left: 0px;
right: 0px; right: 0px;
bottom: 0px; bottom: 0px;
padding: 10px 20px; padding: 10px 20px;
margin: 0; margin: 0;
background: @gray-10; background: @gray-10;
border-top: 1px solid @purple-l3; border-top: 1px solid @purple-l3;
} }
/*we will always make sure to wrap iframe dialogs in proper padding*/ .umb-dialog .propertyItemheader {
.umbracoDialog{ width: 140px !Important;
width: auto !Important; }
height: auto !Important;
padding: 20px; .umb-dialog .diffDropdown
} {
.umbracoDialog .umb-pane{margin-left: 0px; margin-right: 0px; margin-top: 0px;} width:400px;
.umbracoDialog .umb-dialog-body .umb-pane{margin-left: 20px; margin-right: 20px; margin-top: 20px;} }
.umbracoDialog form{height: 100%;}
.umb-dialog .diffPanel {
/*ensures dialogs doesnt have side-by-side labels*/ height: 400px;
.umbracoDialog .controls-row, }
.umb-modal .controls-row{margin-left: 0px !important;}
/* modal and umb-modal are used for right.hand dialogs */ .umb-dialog .diff {
.modal { margin-top: 10px;
border-radius: 0 !important; height: 100%;
overflow: auto;
&.fade.in{border: none !important;} border-top: 1px solid #ccc;
} border-top: 1px solid #ccc;
.umb-modal.fade { padding: 5px;
outline: none; }
top: 0 !important; .umb-dialog .diff table{
left: -100% !important; width:95%;
width: 0px !important; max-width:95%;
-webkit-transition: left 0.3s linear, left 0.3s ease-out; margin: 0 3px;
-moz-transition: opacity 0.3s linear, top 0.3s ease-out; }
-o-transition: opacity 0.3s linear, top 0.3s ease-out; .umb-dialog .diff table th {
transition: opacity 0.3s linear, top 0.3s ease-out; padding: 5px;
height: 100% !important; width: 25%;
} border-bottom: 1px solid #ccc;
}
.umb-modal.fade.in {
top: 0 !important; .umb-dialog .diff table td {
left: 100% !important; border-bottom: 1px solid #ccc;
margin-left: -440px; padding: 3px;
width: 440px !important; }
height: 100% !important;
display: block; .umb-dialog .diff del {
} background: rgb(255, 230, 230) none repeat scroll 0%;
-moz-background-clip: -moz-initial;
.umb-modal-left.fade { -moz-background-origin: -moz-initial;
top: 0 !important; -moz-background-inline-policy: -moz-initial;
left: -100% !important; }
width: 0px !important;
-webkit-transition: left 0.3s linear, left 0.3s ease-out; .umb-dialog .diff ins {
-moz-transition: opacity 0.3s linear, top 0.3s ease-out; background: rgb(230, 255, 230) none repeat scroll 0%;
-o-transition: opacity 0.3s linear, top 0.3s ease-out; -moz-background-clip: -moz-initial;
transition: opacity 0.3s linear, top 0.3s ease-out; -moz-background-origin: -moz-initial;
height: 100% !important; -moz-background-inline-policy: -moz-initial;
} }
.umb-modal-left.fade.in {
top: 0 !important; .umb-dialog .diff .diffnotice {
left: 0 !important; text-align: center;
margin-left: 80px; margin-bottom: 10px;
width: 440px !important; }
height: 100% !important;
display: block; /*we will always make sure to wrap iframe dialogs in proper padding*/
} .umbracoDialog{
width: auto !Important;
/*Modal default panel styles*/ height: auto !Important;
.umb-modal .umb-panel-header { padding: 20px;
padding: 20px; }
background: @white; .umbracoDialog .umb-pane{margin-left: 0px; margin-right: 0px; margin-top: 0px;}
border: none; .umbracoDialog .umb-dialog-body .umb-pane{margin-left: 20px; margin-right: 20px; margin-top: 20px;}
height: auto; .umbracoDialog form{height: 100%;}
}
.umb-modal .umb-panel-body{ /*ensures dialogs doesnt have side-by-side labels*/
padding: 0px 20px 0px 20px; .umbracoDialog .controls-row,
} .umb-modal .controls-row{margin-left: 0px !important;}
.umb-modal.fade.in.wide { /* modal and umb-modal are used for right.hand dialogs */
margin-left: -640px; .modal {
width: 640px !important; border-radius: 0 !important;
}
.umb-modal i { &.fade.in{border: none !important;}
font-size: 20px; }
} .umb-modal.fade {
.umb-modal .breadcrumb { outline: none;
background: none; top: 0 !important;
padding: 0 left: -100% !important;
} width: 0px !important;
-webkit-transition: left 0.3s linear, left 0.3s ease-out;
.umb-modal .breadcrumb input { -moz-transition: opacity 0.3s linear, top 0.3s ease-out;
height: 12px -o-transition: opacity 0.3s linear, top 0.3s ease-out;
} transition: opacity 0.3s linear, top 0.3s ease-out;
height: 100% !important;
.umb-modal.ysod { }
z-index: 10000;
} .umb-modal.fade.in {
top: 0 !important;
left: 100% !important;
margin-left: -440px;
width: 440px !important;
height: 100% !important;
display: block;
}
.umb-modal-left.fade {
top: 0 !important;
left: -100% !important;
width: 0px !important;
-webkit-transition: left 0.3s linear, left 0.3s ease-out;
-moz-transition: opacity 0.3s linear, top 0.3s ease-out;
-o-transition: opacity 0.3s linear, top 0.3s ease-out;
transition: opacity 0.3s linear, top 0.3s ease-out;
height: 100% !important;
}
.umb-modal-left.fade.in {
top: 0 !important;
left: 0 !important;
margin-left: 80px;
width: 440px !important;
height: 100% !important;
display: block;
}
/*Modal default panel styles*/
.umb-modal .umb-panel-header {
padding: 20px;
background: @white;
border: none;
height: auto;
}
.umb-modal .umb-panel-body{
padding: 0px 20px 0px 20px;
}
.umb-modal.fade.in.wide {
margin-left: -640px;
width: 640px !important;
}
.umb-modal i {
font-size: 20px;
}
.umb-modal .breadcrumb {
background: none;
padding: 0
}
.umb-modal .breadcrumb input {
height: 12px
}
.umb-modal.ysod {
z-index: 10000;
}
+10 -10
View File
@@ -16,7 +16,7 @@
.umb-panel-header { .umb-panel-header {
background: @gray-10; background: @gray-10;
border-bottom: 1px solid @purple-l3; border-bottom: 1px solid @gray-8;
position: absolute; position: absolute;
height: 99px; height: 99px;
top: 0px; top: 0px;
@@ -43,23 +43,23 @@
} }
.umb-mediapicker-upload { .umb-mediapicker-upload {
display: -ms-flexbox;
display: -webkit-box;
display: -webkit-flex;
display: flex; display: flex;
.form-search { .form-search {
-webkit-flex: 1;
-ms-flex: 1;
flex: 1; flex: 1;
&__toggle{ &__toggle{
margin: 10px 0; margin: 10px 0;
display: flex;
align-items: center;
input { label{
margin: 0; margin-right: 10px;
} position: relative;
top: -2px;
label {
margin-left: 5px;
margin-bottom: 0;
} }
} }
} }
@@ -102,11 +102,11 @@ div.umb-codeeditor {
border: 1px solid @gray-8; border: 1px solid @gray-8;
} }
div.umb-codeeditor .umb-el-wrap { div.umb-codeeditor .umb-el-wrap {
padding: 0; padding: 0px;
} }
div.umb-codeeditor .umb-btn-toolbar { div.umb-codeeditor .umb-btn-toolbar {
padding: 0; padding: 0px;
margin: 0; margin: 0px;
border-bottom: @gray-8 1px solid; border-bottom: @gray-8 1px solid;
background: @gray-10; background: @gray-10;
} }
@@ -122,7 +122,7 @@ div.umb-codeeditor .umb-btn-toolbar {
position: absolute; position: absolute;
} }
} }
.mce-tinymce{border: 1px solid @gray-8 !important; border-radius: 0 !important;} .mce-tinymce{border: 1px solid @gray-8 !important; border-radius: 0px !important;}
.mce-panel{background: @gray-10 !important; border-color: @gray-8 !important;} .mce-panel{background: @gray-10 !important; border-color: @gray-8 !important;}
.mce-btn-group, .mce-btn{border: none !important; background: none !important;} .mce-btn-group, .mce-btn{border: none !important; background: none !important;}
.mce-ico{font-size: 12px !important; color: @gray-1 !important;} .mce-ico{font-size: 12px !important; color: @gray-1 !important;}
@@ -152,7 +152,6 @@ div.umb-codeeditor .umb-btn-toolbar {
border-radius: 3px; border-radius: 3px;
margin-top: auto; margin-top: auto;
margin-bottom: auto; margin-bottom: auto;
flex: 0 0 auto;
} }
.handle { .handle {
@@ -204,7 +203,7 @@ div.umb-codeeditor .umb-btn-toolbar {
} }
label { label {
border: 1px solid @white; border: 1px solid #fff;
padding: 6px 10px; padding: 6px 10px;
font-family: monospace; font-family: monospace;
border: 1px solid #dfdfe1; border: 1px solid #dfdfe1;
@@ -214,49 +213,6 @@ div.umb-codeeditor .umb-btn-toolbar {
} }
} }
//
// Image Cropper
// --------------------------------------------------
.umb-prevalues-multivalues.umb-cropsizes{
max-width: 500px;
width: 100%;
min-width: 66.6%;
@media (min-width: 1101px) and (max-width: 1300px), (max-width: 930px) {
max-width: none;
}
.umb-overlay__form & {
width: 100%;
}
}
.umb-cropsizes {
&__add {
display: inline-flex;
align-items: center;
}
&__controls {
margin: 24px 0 0;
display: flex;
}
&__input {
width: 100%;
&-wrap{
flex: 1 1 auto;
margin-right: 10px;
&--narrow {
flex: 0 1 100px;
}
}
}
}
// //
// Media picker // Media picker
@@ -279,18 +235,6 @@ div.umb-codeeditor .umb-btn-toolbar {
} }
} }
.umb-mediapicker .label{
&__trashed{
background-color: @red;
position: absolute;
top: 50%;
left: 50%;
z-index: 1;
transform: translate3d(-50%,-50%,0);
margin: 0;
}
}
.umb-mediapicker .picked-image { .umb-mediapicker .picked-image {
position: absolute; position: absolute;
bottom: 10px; bottom: 10px;
@@ -389,7 +333,7 @@ div.umb-codeeditor .umb-btn-toolbar {
background-image: url(../img/checkered-background.png); background-image: url(../img/checkered-background.png);
} }
.umb-sortable-thumbnails li .trashed { .umb-sortable-thumbnails li img.trashed {
opacity:0.3; opacity:0.3;
} }
@@ -763,8 +707,8 @@ div.umb-codeeditor .umb-btn-toolbar {
.umb-photo-folder .picrow div, .umb-photo-preview{ .umb-photo-folder .picrow div, .umb-photo-preview{
margin: 0; margin: 0px;
padding: 0; padding: 0px;
border: none; border: none;
display: inline-block; display: inline-block;
vertical-align: top; vertical-align: top;
@@ -801,9 +745,9 @@ div.umb-codeeditor .umb-btn-toolbar {
//this is a temp hack, to provide selectors in the dialog: //this is a temp hack, to provide selectors in the dialog:
.umb-photo-folder .pic:hover .selector-overlay { .umb-photo-folder .pic:hover .selector-overlay {
position: absolute; position: absolute;
bottom: 0; bottom: 0px;
left: 0; left: 0px;
right: 0; right: 0px;
padding: 5px; padding: 5px;
background: @black; background: @black;
z-index: 100; z-index: 100;
@@ -883,7 +827,7 @@ div.umb-codeeditor .umb-btn-toolbar {
.umb-fileupload ul { .umb-fileupload ul {
list-style: none; list-style: none;
vertical-align: middle; vertical-align: middle;
margin-bottom: 0; margin-bottom: 0px;
} }
.umb-fileupload label { .umb-fileupload label {
@@ -1041,20 +985,7 @@ div.umb-codeeditor .umb-btn-toolbar {
// //
// Nested boolean (e.g. list view bulk action permissions) // Nested boolean (e.g. list view bulk action permissions)
// ------------------------------------------------------- // ---------------------=====-----------------------------
.umb-nested-boolean label {margin-bottom: 8px; float: left; width: 320px;} .umb-nested-boolean label {margin-bottom: 8px; float: left; width: 320px;}
.umb-nested-boolean label span {float: left; width: 80%;} .umb-nested-boolean label span {float: left; width: 80%;}
.umb-nested-boolean label input[type='checkbox'] {margin-right: 10px; float: left;} .umb-nested-boolean label input[type='checkbox'] {margin-right: 10px; float: left;}
//
// Custom styles of property editors in property preview in document type editor
// -----------------------------------------------------------------------------
.umb-group-builder__property-preview {
.umb-property-editor {
.slider {
.tooltip {
display: none;
}
}
}
}
@@ -207,7 +207,7 @@
} }
} }
$scope.allowPasswordReset = Umbraco.Sys.ServerVariables.umbracoSettings.canSendRequiredEmail && Umbraco.Sys.ServerVariables.umbracoSettings.allowPasswordReset; $scope.allowPasswordReset = Umbraco.Sys.ServerVariables.umbracoSettings.allowPasswordReset;
$scope.showLogin = function () { $scope.showLogin = function () {
$scope.errorMsg = ""; $scope.errorMsg = "";
@@ -88,7 +88,13 @@
<div class="flex justify-center items-center"> <div class="flex justify-center items-center">
<umb-button type="button" <umb-button type="button"
button-style="success" button-style="success"
label="Done" label="Get started"
action="getStarted()">
</umb-button>
<umb-button ng-if="!avatarFile.uploaded"
type="button"
button-style="link"
label="Skip"
action="getStarted()"> action="getStarted()">
</umb-button> </umb-button>
</div> </div>
@@ -8,7 +8,7 @@ angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController",
$scope.section = dialogOptions.section; $scope.section = dialogOptions.section;
$scope.treeAlias = dialogOptions.treeAlias; $scope.treeAlias = dialogOptions.treeAlias;
$scope.multiPicker = dialogOptions.multiPicker; $scope.multiPicker = dialogOptions.multiPicker;
$scope.hideHeader = (typeof dialogOptions.hideHeader) === "boolean" ? dialogOptions.hideHeader : true;; $scope.hideHeader = true;
$scope.searchInfo = { $scope.searchInfo = {
searchFromId: dialogOptions.startNodeId, searchFromId: dialogOptions.startNodeId,
searchFromName: null, searchFromName: null,
@@ -428,4 +428,4 @@ angular.module("umbraco").controller("Umbraco.Dialogs.TreePickerController",
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler); $scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler); $scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
}); });
}); });
@@ -1,28 +1,13 @@
(function() { (function() {
"use strict"; "use strict";
function CompositionsOverlay($scope,$location,$filter) { function CompositionsOverlay($scope,$location) {
var vm = this; var vm = this;
vm.isSelected = isSelected; vm.isSelected = isSelected;
vm.openContentType = openContentType; vm.openContentType = openContentType;
// group the content types by their container paths
vm.availableGroups = $filter("orderBy")(
_.map(
_.groupBy($scope.model.availableCompositeContentTypes, function (compositeContentType) {
return compositeContentType.contentType.metaData.containerPath;
}), function(group) {
return {
containerPath: group[0].contentType.metaData.containerPath,
compositeContentTypes: group
};
}
), function (group) {
return group.containerPath.replace(/\//g, " ");
});
function isSelected(alias) { function isSelected(alias) {
if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) { if($scope.model.contentType.compositeContentTypes.indexOf(alias) !== -1) {
return true; return true;
@@ -32,32 +32,27 @@
<li class="umb-checkbox-list__item" ng-repeat="contentTypeEntity in model.whereCompositionUsed"><a ng-click="vm.openContentType(contentTypeEntity.contentType, model.section)"><i class="{{contentTypeEntity.contentType.icon}}"></i>&nbsp;{{contentTypeEntity.contentType.name}}</a></li> <li class="umb-checkbox-list__item" ng-repeat="contentTypeEntity in model.whereCompositionUsed"><a ng-click="vm.openContentType(contentTypeEntity.contentType, model.section)"><i class="{{contentTypeEntity.contentType.icon}}"></i>&nbsp;{{contentTypeEntity.contentType.name}}</a></li>
</ul> </ul>
</div> </div>
<div ng-if="vm.availableGroups.length > 0"> <ul class="umb-checkbox-list">
<ul class="umb-checkbox-list" ng-repeat="group in vm.availableGroups | filter:searchTerm"> <li class="umb-checkbox-list__item"
<li style="font-weight: bold" ng-show="vm.availableGroups.length > 1"> ng-repeat="compositeContentType in model.availableCompositeContentTypes | filter:searchTerm"
<i class="icon-folder umb-checkbox-list__item-icon"></i> ng-class="{'-disabled': compositeContentType.allowed===false || compositeContentType.inherited, '-selected': vm.isSelected(compositeContentType.contentType.alias)}">
{{group.containerPath}}
</li>
<li class="umb-checkbox-list__item"
ng-repeat="compositeContentType in group.compositeContentTypes | orderBy:'contentType.name' | filter:searchTerm"
ng-class="{'-disabled': compositeContentType.allowed===false || compositeContentType.inherited, '-selected': vm.isSelected(compositeContentType.contentType.alias)}">
<div class="umb-checkbox-list__item-checkbox" <div class="umb-checkbox-list__item-checkbox"
ng-class="{ '-selected': model.compositeContentTypes.indexOf(compositeContentType.contentType.alias)+1 }"> ng-class="{ '-selected': model.compositeContentTypes.indexOf(compositeContentType.contentType.alias)+1 }">
<input type="checkbox" <input type="checkbox"
id="umb-overlay-comp-{{compositeContentType.contentType.key}}" id="umb-overlay-comp-{{compositeContentType.contentType.key}}"
checklist-model="model.compositeContentTypes" checklist-model="model.compositeContentTypes"
checklist-value="compositeContentType.contentType.alias" checklist-value="compositeContentType.contentType.alias"
ng-change="model.selectCompositeContentType(compositeContentType.contentType)" ng-change="model.selectCompositeContentType(compositeContentType.contentType)"
ng-disabled="compositeContentType.allowed===false || compositeContentType.inherited" /> ng-disabled="compositeContentType.allowed===false || compositeContentType.inherited" />
</div> </div>
<label for="umb-overlay-comp-{{compositeContentType.contentType.key}}" class="umb-checkbox-list__item-text" ng-class="{'-faded': compositeContentType.allowed===false}"> <label for="umb-overlay-comp-{{compositeContentType.contentType.key}}" class="umb-checkbox-list__item-text" ng-class="{'-faded': compositeContentType.allowed===false}">
<i class="{{ compositeContentType.contentType.icon }} umb-checkbox-list__item-icon"></i> <i class="{{ compositeContentType.contentType.icon }} umb-checkbox-list__item-icon"></i>
{{ compositeContentType.contentType.name }} {{ compositeContentType.contentType.name }}
<span class="umb-checkbox-list__item-caption" ng-if="compositeContentType.inherited">(inherited)</span> <span class="umb-checkbox-list__item-caption" ng-if="compositeContentType.inherited">(inherited)</span>
</label> </label>
</li>
</ul> </li>
</div> </ul>
</div> </div>
@@ -46,8 +46,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
// if a link exists, get the properties to build the anchor name list // if a link exists, get the properties to build the anchor name list
contentResource.getById(id).then(function (resp) { contentResource.getById(id).then(function (resp) {
$scope.model.target.url = resp.urls[0];
$scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties)); $scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties));
$scope.model.target.url = resp.urls[0];
}); });
} else if ($scope.model.target.url.length) { } else if ($scope.model.target.url.length) {
// a url but no id/udi indicates an external link - trim the url to remove the anchor/qs // a url but no id/udi indicates an external link - trim the url to remove the anchor/qs
@@ -87,8 +87,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.LinkPickerController",
$scope.model.target.url = "/"; $scope.model.target.url = "/";
} else { } else {
contentResource.getById(args.node.id).then(function (resp) { contentResource.getById(args.node.id).then(function (resp) {
$scope.model.target.url = resp.urls[0];
$scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties)); $scope.anchorValues = tinyMceService.getAnchorNames(JSON.stringify(resp.properties));
$scope.model.target.url = resp.urls[0];
}); });
} }
@@ -309,9 +309,21 @@ angular.module("umbraco")
debounceSearchMedia(); debounceSearchMedia();
}; };
$scope.toggle = function() { /**
* Toggle the $scope.model.allowAsRoot value to either true or false
*/
$scope.toggle = function(){
// Make sure to activate the changeSearch function everytime the toggle is clicked // Make sure to activate the changeSearch function everytime the toggle is clicked
$scope.changeSearch(); $scope.changeSearch();
// Toggle the showChilds option
if($scope.showChilds){
$scope.showChilds = false;
return;
}
$scope.showChilds = true;
} }
$scope.changePagination = function(pageNumber) { $scope.changePagination = function(pageNumber) {
@@ -22,10 +22,14 @@
no-dirty-check /> no-dirty-check />
<div class="form-search__toggle"> <div class="form-search__toggle">
<input type="checkbox" ng-model="showChilds" ng-change="toggle()" />
<label> <label>
<localize key="general_includeFromsubFolders">Include subfolders in search</localize> <localize key="general_includeFromsubFolders">Include subfolders in search</localize>
</label> </label>
<umb-toggle
checked="showChilds"
on-click="toggle()">
</umb-toggle>
</div> </div>
</div> </div>
@@ -1,7 +0,0 @@
<div>
<form ng-submit="model.submit(model)">
<umb-property property="property" ng-repeat="property in model.properties">
<umb-property-editor model="property"></umb-property-editor>
</umb-property>
</form>
</div>
@@ -6,9 +6,9 @@
</span> </span>
<div class="umb-toggle__toggle"> <div class="umb-toggle__toggle">
<i ng-show="checked && hideIcons !== 'true'" class="umb-toggle__icon umb-toggle__icon--left icon-check"></i> <i ng-show="checked && !hideIcons" class="umb-toggle__icon umb-toggle__icon--left icon-check"></i>
<div class="umb-toggle__handler shadow-depth-1"></div> <div class="umb-toggle__handler shadow-depth-1"></div>
<i ng-show="!checked && hideIcons !== 'true'" class="umb-toggle__icon umb-toggle__icon--right icon-wrong"></i> <i ng-show="!checked && !hideIcons" class="umb-toggle__icon umb-toggle__icon--right icon-wrong"></i>
</div> </div>
<span ng-if="labelPosition === 'right' && showLabels === 'true'"> <span ng-if="labelPosition === 'right' && showLabels === 'true'">
@@ -5,7 +5,7 @@
<img ng-show="isCroppable" ng-src="{{src}}" style="max-width: 100%; max-height: 100%" ng-click="setFocalPoint($event)" draggable="false"/> <img ng-show="isCroppable" ng-src="{{src}}" style="max-width: 100%; max-height: 100%" ng-click="setFocalPoint($event)" draggable="false"/>
<img ng-show="!isCroppable && !hasDimensions" ng-src="{{src}}" width="200" height="200" draggable="false" style="cursor: default;"/> <img ng-show="!isCroppable && !hasDimensions" ng-src="{{src}}" width="200" height="200" draggable="false" style="cursor: default;"/>
<div ng-show="isCroppable" class="overlay" ng-style="style()"> <div ng-show="isCroppable" ng-when="isCroppable" class="overlay" ng-style="style()">
</div> </div>
@@ -1,12 +1,12 @@
<div class="umb-color-swatches" ng-class="{ 'with-labels': useLabel }"> <div class="umb-color-swatches" ng-class="{ 'with-labels': useLabel }">
<button type="button" class="umb-color-box umb-color-box--{{size}} btn-{{color.value}}" ng-repeat="color in colors" title="{{useLabel || useColorClass ? (color.label || color.value) : ('#' + color.value)}}" hex-bg-inline="{{useColorClass === false}}" hex-bg-color="{{color.value}}" ng-class="{ 'active': color.value === selectedColor }" ng-click="setColor(color.value)"> <button type="button" class="umb-color-box umb-color-box--{{size}} btn-{{color.value}}" ng-repeat="color in colors" title="{{useLabel ? color.label : '#' + color.value}}" hex-bg-inline="{{useColorClass === false}}" hex-bg-color="{{color.value}}" ng-class="{ 'active': color.value === selectedColor }" ng-click="setColor(color.value)">
<div class="umb-color-box-inner"> <div class="umb-color-box-inner">
<div class="check_circle"> <div class="check_circle">
<i class="icon icon-check small"></i> <i class="icon icon-check small"></i>
</div> </div>
<div class="umb-color-box__label" ng-if="useLabel"> <div class="umb-color-box__label" ng-if="useLabel">
<div class="umb-color-box__name truncate">{{ color.label || color.value }}</div> <div class="umb-color-box__name truncate">{{ color.label }}</div>
<div class="umb-color-box__description">#{{ color.value }}</div> <div class="umb-color-box__description">#{{ color.value }}</div>
</div> </div>
</div> </div>
@@ -33,10 +33,8 @@
<umb-editor model="preValue" is-pre-value="true"></umb-editor> <umb-editor model="preValue" is-pre-value="true"></umb-editor>
</umb-property> </umb-property>
<div class="text-right"> <button type="button" class="btn" ng-click="toggleEditListViewDataTypeSettings()"><localize key="general_close">Close</localize></button>
<button type="button" class="btn btn-link" ng-click="toggleEditListViewDataTypeSettings()"><localize key="general_close">Close</localize></button> <button type="button" class="btn btn-success" ng-click="saveListViewDataType()"><localize key="buttons_saveListView"></localize></button>
<button type="button" class="btn btn-success" ng-click="saveListViewDataType()"><localize key="buttons_saveListView"></localize></button>
</div>
</div> </div>
</div> </div>
@@ -37,6 +37,12 @@
</div> </div>
</div> </div>
<div ng-show="hideDropzone" ngf-drag-over-class="hide" class="text-center">
<small>
<localize key="media_dragFilesHereToUpload">You can drag files here to upload</localize>
</small>
</div>
<!-- List of uploading/uploaded files --> <!-- List of uploading/uploaded files -->
<ul class="file-list" ng-show="done.length > 0 || queue.length > 0 || rejected.length > 0 || filesHolder.length > 0"> <ul class="file-list" ng-show="done.length > 0 || queue.length > 0 || rejected.length > 0 || filesHolder.length > 0">
@@ -30,21 +30,15 @@ function ContentDeleteController($scope, contentResource, treeService, navigatio
var recycleBin = treeService.getDescendantNode(rootNode, -20); var recycleBin = treeService.getDescendantNode(rootNode, -20);
if (recycleBin) { if (recycleBin) {
recycleBin.hasChildren = true; recycleBin.hasChildren = true;
//reload the recycle bin if it's already expanded so the deleted item is shown
if (recycleBin.expanded) {
treeService.loadNodeChildren({ node: recycleBin, section: "content" });
}
} }
} }
//if the current edited item is the same one as we're deleting, we need to navigate elsewhere //if the current edited item is the same one as we're deleting, we need to navigate elsewhere
if (editorState.current && editorState.current.id == $scope.currentNode.id) { if (editorState.current && editorState.current.id == $scope.currentNode.id) {
//If the deleted item lived at the root then just redirect back to the root, otherwise redirect to the item's parent //If the deleted item lived at the root then just redirect back to the root, otherwise redirect to the item's parent
var location = "/content"; var location = "/content";
if ($scope.currentNode.parentId.toString() === "-20") if ($scope.currentNode.parentId.toString() !== "-1")
location = "/content/content/recyclebin";
else if ($scope.currentNode.parentId.toString() !== "-1")
location = "/content/content/edit/" + $scope.currentNode.parentId; location = "/content/content/edit/" + $scope.currentNode.parentId;
$location.path(location); $location.path(location);
@@ -1,95 +1,22 @@
angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController", angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController",
function ($scope, relationResource, contentResource, navigationService, appState, treeService, userService) { function ($scope, relationResource, contentResource, navigationService, appState, treeService, localizationService) {
var dialogOptions = $scope.dialogOptions; var dialogOptions = $scope.dialogOptions;
$scope.source = _.clone(dialogOptions.currentNode); var node = dialogOptions.currentNode;
$scope.error = null; $scope.error = null;
$scope.loading = true;
$scope.moving = false;
$scope.success = false; $scope.success = false;
$scope.dialogTreeEventHandler = $({}); relationResource.getByChildId(node.id, "relateParentDocumentOnDelete").then(function (data) {
$scope.searchInfo = {
showSearch: false,
results: [],
selectedSearchResults: []
}
$scope.treeModel = {
hideHeader: false
}
userService.getCurrentUser().then(function (userData) {
$scope.treeModel.hideHeader = userData.startContentIds.length > 0 && userData.startContentIds.indexOf(-1) == -1;
});
function nodeSelectHandler(ev, args) { if (data.length == 0) {
$scope.success = false;
if (args && args.event) { $scope.error = {
args.event.preventDefault(); errorMsg: localizationService.localize('recycleBin_itemCannotBeRestored'),
args.event.stopPropagation(); data: {
} Message: localizationService.localize('recycleBin_noRestoreRelation')
}
if ($scope.target) { }
//un-select if there's a current one selected
$scope.target.selected = false;
}
$scope.target = args.node;
$scope.target.selected = true;
}
function nodeExpandedHandler(ev, args) {
// open mini list view for list views
if (args.node.metaData.isContainer) {
openMiniListView(args.node);
}
}
$scope.hideSearch = function () {
$scope.searchInfo.showSearch = false;
$scope.searchInfo.results = [];
}
// method to select a search result
$scope.selectResult = function (evt, result) {
result.selected = result.selected === true ? false : true;
nodeSelectHandler(evt, { event: evt, node: result });
};
//callback when there are search results
$scope.onSearchResults = function (results) {
$scope.searchInfo.results = results;
$scope.searchInfo.showSearch = true;
};
$scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
$scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
$scope.$on('$destroy', function () {
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
});
// Mini list view
$scope.selectListViewNode = function (node) {
node.selected = node.selected === true ? false : true;
nodeSelectHandler({}, { node: node });
};
$scope.closeMiniListView = function () {
$scope.miniListView = undefined;
};
function openMiniListView(node) {
$scope.miniListView = node;
}
relationResource.getByChildId($scope.source.id, "relateParentDocumentOnDelete").then(function (data) {
$scope.loading = false;
if (!data.length) {
$scope.moving = true;
return; return;
} }
@@ -98,37 +25,39 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController"
if ($scope.relation.parentId == -1) { if ($scope.relation.parentId == -1) {
$scope.target = { id: -1, name: "Root" }; $scope.target = { id: -1, name: "Root" };
} else { } else {
$scope.loading = true; contentResource.getById($scope.relation.parentId).then(function (data) {
$scope.target = data;
contentResource.getById($scope.relation.parentId).then(function (data) {
$scope.loading = false;
$scope.target = data;
// make sure the target item isn't in the recycle bin // make sure the target item isn't in the recycle bin
if ($scope.target.path.indexOf("-20") !== -1) { if($scope.target.path.indexOf("-20") !== -1) {
$scope.moving = true; $scope.error = {
$scope.target = null; errorMsg: localizationService.localize('recycleBin_itemCannotBeRestored'),
} data: {
Message: localizationService.localize('recycleBin_restoreUnderRecycled').then(function (value) {
value.replace('%0%', $scope.target.name);
})
}
};
$scope.success = false;
}
}, function (err) { }, function (err) {
$scope.loading = false; $scope.success = false;
$scope.error = err; $scope.error = err;
}); });
} }
}, function (err) { }, function (err) {
$scope.loading = false; $scope.success = false;
$scope.error = err; $scope.error = err;
}); });
$scope.restore = function () { $scope.restore = function () {
$scope.loading = true;
// this code was copied from `content.move.controller.js` // this code was copied from `content.move.controller.js`
contentResource.move({ parentId: $scope.target.id, id: $scope.source.id }) contentResource.move({ parentId: $scope.target.id, id: node.id })
.then(function (path) { .then(function (path) {
$scope.loading = false;
$scope.success = true; $scope.success = true;
//first we need to remove the node that launched the dialog //first we need to remove the node that launched the dialog
@@ -149,7 +78,7 @@ angular.module("umbraco").controller("Umbraco.Editors.Content.RestoreController"
}); });
}, function (err) { }, function (err) {
$scope.loading = false; $scope.success = false;
$scope.error = err; $scope.error = err;
}); });
}; };
@@ -83,10 +83,10 @@
</div> </div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success"> <div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success">
<a class="btn btn-link" ng-click="nav.hideDialog()" ng-show="!busy"> <a class="btn btn-link" ng-click="nav.hideDialog()" ng-if="!busy">
<localize key="general_cancel">Cancel</localize> <localize key="general_cancel">Cancel</localize>
</a> </a>
<button class="btn btn-primary" ng-click="copy()" ng-disabled="busy || !target"> <button class="btn btn-primary" ng-click="copy()" ng-disabled="busy">
<localize key="actions_copy">Copy</localize> <localize key="actions_copy">Copy</localize>
</button> </button>
</div> </div>
@@ -7,7 +7,7 @@
<p class="umb-abstract"> <p class="umb-abstract">
<localize key="defaultdialogs_recycleBinWarning">When items are deleted from the recycle bin, they will be gone forever</localize>. <localize key="defaultdialogs_recycleBinWarning">When items are deleted from the recycle bin, they will be gone forever</localize>.
<localize key="general_areyousure">Are you sure?</localize> <localize key="general_areyousure">Are you sure</localize>?
</p> </p>
<umb-confirm on-confirm="performDelete" on-cancel="cancel"> <umb-confirm on-confirm="performDelete" on-cancel="cancel">
@@ -73,11 +73,11 @@
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success"> <div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success">
<a class="btn btn-link" ng-click="nav.hideDialog()" ng-show="!busy"> <a class="btn btn-link" ng-click="nav.hideDialog()" ng-if="!busy">
<localize key="general_cancel">Cancel</localize> <localize key="general_cancel">Cancel</localize>
</a> </a>
<button class="btn btn-primary" ng-click="move()" ng-disabled="busy || !target"> <button class="btn btn-primary" ng-click="move()" ng-disabled="busy">
<localize key="actions_move">Move</localize> <localize key="actions_move">Move</localize>
</button> </button>
</div> </div>
</div> </div>
@@ -1,48 +1,44 @@
<div ng-controller="Umbraco.Editors.Content.CreateNotifyController as vm"> <div ng-controller="Umbraco.Editors.Content.CreateNotifyController as vm">
<umb-load-indicator ng-if="vm.loading"></umb-load-indicator> <umb-load-indicator ng-if="vm.loading"></umb-load-indicator>
<div class="umb-dialog-body"> <div class="umb-pane" ng-show="!vm.loading">
<div class="umb-pane" ng-show="!vm.loading"> <form name="notifyForm"
<form name="notifyForm" novalidate
novalidate val-form-manager>
val-form-manager> <div ng-show="vm.saveError" ng-cloak>
<div ng-show="vm.saveError" ng-cloak> <div class="alert alert-error">
<div class="alert alert-error"> <div><strong>{{vm.saveError.errorMsg}}</strong></div>
<div><strong>{{vm.saveError.errorMsg}}</strong></div> <div>{{vm.saveError.data.message}}</div>
<div>{{vm.saveError.data.message}}</div>
</div>
</div> </div>
<div ng-show="vm.saveSuccces" ng-cloak> </div>
<div class="alert alert-success"> <div ng-show="vm.saveSuccces" ng-cloak>
<localize key="notify_notificationsSavedFor"></localize><strong> {{currentNode.name}}</strong> <div class="alert alert-success">
</div> <localize key="notify_notificationsSavedFor"></localize><strong> {{currentNode.name}}</strong>
<button class="btn btn-primary" ng-click="vm.cancel()"><localize key="general_ok">Ok</localize></button>
</div> </div>
<div ng-hide="vm.saveSuccces || vm.saveError" ng-cloak> </div>
<div class="block-form" ng-show="!vm.loading"> <div ng-cloak>
<localize key="notify_notifySet">Set your notification for</localize> <strong>{{ currentNode.name }}</strong> <div class="block-form" ng-show="!vm.loading">
<umb-control-group> <h5><localize key="notify_notifySet">Set your notification for</localize> {{ currentNode.name }}</h5>
<umb-permission ng-repeat="option in vm.notifyOptions" <umb-control-group>
name="option.name" <umb-permission ng-repeat="option in vm.notifyOptions"
description="option.description" name="option.name"
selected="option.checked"> description="option.description"
</umb-permission> selected="option.checked">
</umb-control-group> </umb-permission>
</div> </umb-control-group>
</div> </div>
</form> </div>
</div> <div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
</div> <umb-button label-key="general_cancel"
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="vm.saveSuccces || vm.saveError"> action="vm.cancel()"
<umb-button label-key="general_cancel" type="button"
action="vm.cancel()" button-style="link">
type="button" </umb-button>
button-style="link"> <umb-button label-key="buttons_save"
</umb-button> type="button"
<umb-button label-key="buttons_save" action="vm.save(vm.notifyOptions)"
type="button" button-style="success">
action="vm.save(vm.notifyOptions)" </umb-button>
state="vm.saveState" </div>
button-style="success"> </form>
</umb-button>
</div> </div>
</div> </div>
@@ -1,92 +1,26 @@
<div ng-controller="Umbraco.Editors.Content.RestoreController"> <div ng-controller="Umbraco.Editors.Content.RestoreController">
<div class="umb-dialog-body" ng-cloak> <div class="umb-dialog-body">
<umb-pane> <umb-pane>
<umb-load-indicator
ng-if="loading">
</umb-load-indicator>
<div ng-show="error"> <p class="abstract" ng-hide="error != null || success == true">
<div class="alert alert-error"> <localize key="actions_restore">Restore</localize> <strong>{{currentNode.name}}</strong> <localize key="general_under">under</localize> <strong>{{target.name}}</strong>?
<div><strong>{{error.errorMsg}}</strong></div> </p>
<div>{{error.data.Message}}</div>
</div>
</div>
<div ng-show="success"> <div class="alert alert-error" ng-show="error != null">
<div class="alert alert-success"> <div><strong>{{error.errorMsg}}</strong></div>
<strong>{{source.name}}</strong> <div>{{error.data.Message}}</div>
<span ng-hide="moving"><localize key="recycleBin_wasRestored">was restored under</localize></span> </div>
<span ng-show="moving"><localize key="editdatatype_wasMoved">was moved underneath</localize></span>
<strong>{{target.name}}</strong>
</div>
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
</div>
<div ng-hide="moving || loading || success"> <div class="alert alert-success" ng-show="success == true">
<p><strong>{{currentNode.name}}</strong> <localize key="editdatatype_wasMoved">was moved underneath</localize> <strong>{{target.name}}</strong></p>
<p class="abstract" ng-hide="error || success"> <button class="btn btn-primary" ng-click="nav.hideDialog()"><localize key="general_ok">OK</localize></button>
<localize key="actions_restore">Restore</localize> <strong>{{source.name}}</strong> <localize key="general_under">under</localize> <strong>{{target.name}}</strong>? </div>
</p>
</div>
<div ng-hide="!moving || loading || success">
<div>
<div class="alert alert-info">
<div><strong><localize key="recycleBin_itemCannotBeRestored">Cannot automatically restore this item</localize></strong></div>
<div><localize key="recycleBin_itemCannotBeRestoredHelpText">There is no location where this item can be automatically restored. You can move the item manually using the tree below.</localize></div>
</div>
</div>
<div ng-hide="miniListView">
<umb-tree-search-box
hide-search-callback="hideSearch"
search-callback="onSearchResults"
show-search="{{searchInfo.showSearch}}"
section="content">
</umb-tree-search-box>
<br />
<umb-tree-search-results
ng-if="searchInfo.showSearch"
results="searchInfo.results"
select-result-callback="selectResult">
</umb-tree-search-results>
<div ng-hide="searchInfo.showSearch">
<umb-tree
section="content"
hideheader="{{treeModel.hideHeader}}"
hideoptions="true"
isdialog="true"
eventhandler="dialogTreeEventHandler"
enablelistviewexpand="true"
enablecheckboxes="true">
</umb-tree>
</div>
</div>
<umb-mini-list-view
ng-if="miniListView"
node="miniListView"
entity-type="Document"
on-select="selectListViewNode(node)"
on-close="closeMiniListView()">
</umb-mini-list-view>
</div>
</umb-pane> </umb-pane>
</div> </div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="loading || moving || success"> <div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success == true">
<a class="btn btn-link" ng-click="nav.hideDialog()"><localize key="general_cancel">Cancel</localize></a> <a class="btn btn-link" ng-click="nav.hideDialog()"><localize key="general_cancel">Cancel</localize></a>
<button class="btn btn-primary" ng-click="restore()" ng-show="error == null"><localize key="actions_restore">Restore</localize></button> <button class="btn btn-primary" ng-click="restore()" ng-show="error == null"><localize key="actions_restore">Restore</localize></button>
</div> </div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="loading || !moving || success">
<a class="btn btn-link" ng-click="nav.hideDialog()"><localize key="general_cancel">Cancel</localize></a>
<button class="btn btn-primary" ng-click="restore()" ng-show="error == null" ng-disabled="!target"><localize key="actions_move">Move</localize></button>
</div>
</div> </div>
@@ -106,7 +106,7 @@
<button <button
type="button" type="button"
class="umb-era-button umb-button--s -green pull-left" class="umb-era-button umb-button--s -green"
ng-class="{ '-inactive': healthCheckAction.providedValue.$invalid}" ng-class="{ '-inactive': healthCheckAction.providedValue.$invalid}"
ng-click="vm.executeAction(check, $parent.$index, action);" ng-click="vm.executeAction(check, $parent.$index, action);"
ng-disabled="healthCheckAction.providedValue.$invalid"> ng-disabled="healthCheckAction.providedValue.$invalid">
@@ -1,20 +1,18 @@
<h3>Hours of Umbraco training videos are only a click away</h3> <h3>Hours of Umbraco training videos are only a click away</h3>
<p>Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit <a href="http://umbraco.tv" target="_blank">umbraco.tv</a> for even more Umbraco videos</p> <p>Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit <a href="http://umbraco.tv" target="_blank">umbraco.tv</a> for even more Umbraco videos</p>
<h4>To get you started:</h4>
<div class="row-fluid" <div class="row-fluid"
ng-init="init('https://umbraco.tv/videos/implementor/chapterrss?sort=no')" ng-init="init('http://umbraco.tv/videos/implementor/chapterrss?sort=no')"
ng-controller="Umbraco.Dashboard.StartupVideosController"> ng-controller="Umbraco.Dashboard.StartupVideosController">
<div ng-show="videos.length"> <ul class="thumbnails" >
<h4>To get you started:</h4> <li class="span2" ng-repeat="video in videos">
<ul class="thumbnails" > <div class="thumbnail" style="margin-right: 20px">
<li class="span2" ng-repeat="video in videos"> <a target="_blank" href="{{video.link}}" title="{{video.title}}">
<div class="thumbnail" style="margin-right: 20px"> <img ng-src="{{video.thumbnail}}" alt="{{video.title}}">
<a target="_blank" href="{{video.link}}" title="{{video.title}}"> </a>
<img ng-src="{{video.thumbnail}}" alt="{{video.title}}"> </div>
</a> </li>
</div> </ul>
</li> </div>
</ul>
</div>
</div>
@@ -1,20 +1,18 @@
<h3>Hours of Umbraco training videos are only a click away</h3> <h3>Hours of Umbraco training videos are only a click away</h3>
<p>Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit <a href="https://umbraco.tv" target="_blank">umbraco.tv</a> for even more Umbraco videos</p> <p>Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit <a href="https://umbraco.tv" target="_blank">umbraco.tv</a> for even more Umbraco videos</p>
<h4>To get you started:</h4>
<div class="row-fluid" <div class="row-fluid"
ng-init="init('https://umbraco.com/feeds/videos/members')" ng-init="init('https://www.umbraco.com/feeds/videos/members')"
ng-controller="Umbraco.Dashboard.StartupVideosController"> ng-controller="Umbraco.Dashboard.StartupVideosController">
<div ng-show="videos.length"> <ul class="thumbnails" >
<h4>To get you started:</h4> <li class="span2" ng-repeat="video in videos">
<ul class="thumbnails" > <div class="thumbnail" style="margin-right: 20px">
<li class="span2" ng-repeat="video in videos"> <a target="_blank" href="{{video.link}}" title="{{video.title}}">
<div class="thumbnail" style="margin-right: 20px"> <img ng-src="{{video.thumbnail}}" alt="{{video.title}}">
<a target="_blank" href="{{video.link}}" title="{{video.title}}"> </a>
<img ng-src="{{video.thumbnail}}" alt="{{video.title}}"> </div>
</a> </li>
</div> </ul>
</li>
</ul>
</div>
</div> </div>
@@ -5,9 +5,9 @@
<h4>Find out more:</h4> <h4>Find out more:</h4>
<ul> <ul>
<li>Read more about working with the items in Settings <a class="btn-link -underline" href="https://our.umbraco.com/documentation/Getting-Started/Backoffice/Sections/" target="_blank">in the Documentation section</a> of Our Umbraco</li> <li>Read more about working with the Items in Settings <a class="btn-link -underline" href="https://our.umbraco.com/documentation/Getting-Started/Backoffice/Sections/" target="_blank">in the Documentation section</a> of Our Umbraco</li>
<li>Download the <a class="btn-link -underline" href="https://our.umbraco.com/projects/website-utilities/umbraco-7-editors-manual" target="_blank">Editors Manual</a> for details on working with the Umbraco UI</li> <li>Download the <a class="btn-link -underline" href="https://our.umbraco.com/projects/website-utilities/umbraco-7-editors-manual" target="_blank">Editors Manual</a> for details on working with the Umbraco UI</li>
<li>Ask a question in the <a class="btn-link -underline" href="https://our.umbraco.com/forum" target="_blank">Community Forum</a></li> <li>Ask a question in the <a class="btn-link -underline" href="https://our.umbraco.com/" target="_blank">Community Forum</a></li>
<li>Watch our <a class="btn-link -underline" href="https://umbraco.tv" target="_blank">tutorial videos</a> (some are free, some require a subscription)</li> <li>Watch our <a class="btn-link -underline" href="https://umbraco.tv" target="_blank">tutorial videos</a> (some are free, some require a subscription)</li>
<li>Find out about our <a class="btn-link -underline" href="https://umbraco.com/products/" target="_blank">productivity boosting tools and commercial support</a></li> <li>Find out about our <a class="btn-link -underline" href="https://umbraco.com/products/" target="_blank">productivity boosting tools and commercial support</a></li>
<li>Find out about real-life <a class="btn-link -underline" href="https://umbraco.com/training/" target="_blank">training and certification</a> opportunities</li> <li>Find out about real-life <a class="btn-link -underline" href="https://umbraco.com/training/" target="_blank">training and certification</a> opportunities</li>
@@ -1,20 +1,18 @@
<h3>Hours of Umbraco training videos are only a click away</h3> <h3>Hours of Umbraco training videos are only a click away</h3>
<p>Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit <a href="http://umbraco.tv" target="_blank">umbraco.tv</a> for even more Umbraco videos</p> <p>Want to master Umbraco? Spend a couple of minutes learning some best practices by watching one of these videos about using Umbraco. And visit <a href="http://umbraco.tv" target="_blank">umbraco.tv</a> for even more Umbraco videos</p>
<h4>To get you started:</h4>
<div class="row-fluid" <div class="row-fluid"
ng-init="init('https://umbraco.tv/videos/implementor/chapterrss?sort=no')" ng-init="init('http://umbraco.tv/videos/implementor/chapterrss?sort=no')"
ng-controller="Umbraco.Dashboard.StartupVideosController"> ng-controller="Umbraco.Dashboard.StartupVideosController">
<div ng-show="videos.length"> <ul class="thumbnails" >
<h4>To get you started:</h4> <li class="span2" ng-repeat="video in videos">
<ul class="thumbnails" > <div class="thumbnail" style="margin-right: 20px">
<li class="span2" ng-repeat="video in videos"> <a target="_blank" href="{{video.link}}" title="{{video.title}}">
<div class="thumbnail" style="margin-right: 20px"> <img ng-src="{{video.thumbnail}}" alt="{{video.title}}">
<a target="_blank" href="{{video.link}}" title="{{video.title}}"> </a>
<img ng-src="{{video.thumbnail}}" alt="{{video.title}}"> </div>
</a> </li>
</div> </ul>
</li> </div>
</ul>
</div>
</div>
@@ -12,16 +12,12 @@
</div> </div>
<div ng-show="error"> <div ng-show="error">
<div class="alert alert-error"> <h5 class="text-error">{{error.errorMsg}}</h5>
<div><strong>{{error.errorMsg}}</strong></div> <p class="text-error">{{error.data.message}}</p>
<div>{{error.data.message}}</div>
</div>
</div> </div>
<div ng-show="success"> <div ng-show="success">
<div class="alert alert-success"> <h5 class="text-success"><strong>{{currentNode.name}}</strong> <localize key="editdatatype_wasMoved">was moved underneath</localize>&nbsp;<strong>{{target.name}}</strong></h5>
<strong>{{currentNode.name}}</strong> <localize key="editdatatype_wasMoved">was moved underneath</localize>&nbsp;<strong>{{target.name}}</strong>
</div>
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button> <button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
</div> </div>
@@ -44,10 +40,10 @@
</div> </div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success"> <div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success">
<a class="btn btn-link" ng-click="nav.hideDialog()" ng-show="!busy"> <a class="btn btn-link" ng-click="nav.hideDialog()" ng-if="!busy">
<localize key="general_cancel">Cancel</localize> <localize key="general_cancel">Cancel</localize>
</a> </a>
<button class="btn btn-primary" ng-click="move()" ng-disabled="busy || !target"> <button class="btn btn-primary" ng-click="move()" ng-disabled="busy">
<localize key="actions_move">Move</localize> <localize key="actions_move">Move</localize>
</button> </button>
</div> </div>
@@ -7,10 +7,8 @@
val-form-manager> val-form-manager>
<div ng-show="error"> <div ng-show="error">
<div class="alert alert-error"> <h5 class="text-error">{{error.errorMsg}}</h5>
<div><strong>{{error.errorMsg}}</strong></div> <p class="text-error">{{error.data.message}}</p>
<div>{{error.data.message}}</div>
</div>
</div> </div>
<umb-control-group label="@renamecontainer_enterNewFolderName" hide-label="false"> <umb-control-group label="@renamecontainer_enterNewFolderName" hide-label="false">
@@ -12,16 +12,13 @@
</div> </div>
<div ng-show="error"> <div ng-show="error">
<div class="alert alert-error"> <h5 class="text-error">{{error.errorMsg}}</h5>
<div><strong>{{error.errorMsg}}</strong></div> <p class="text-error">{{error.data.message}}</p>
<div>{{error.data.message}}</div>
</div>
</div> </div>
<div ng-show="success"> <div ng-show="success">
<div class="alert alert-success"> <h5 class="text-success">
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_copiedUnderneath">was copied underneath</localize>&nbsp;<strong>{{target.name}}</strong> <strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_copiedUnderneath">was copied underneath</localize>&nbsp;<strong>{{target.name}}</strong></h5>
</div>
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button> <button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
</div> </div>
@@ -44,10 +41,10 @@
</div> </div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success"> <div class="umb-dialog-footer btn-toolbar umb-btn-toolbar" ng-hide="success">
<a class="btn btn-link" ng-click="nav.hideDialog()" ng-show="!busy"> <a class="btn btn-link" ng-click="nav.hideDialog()" ng-if="!busy">
<localize key="general_cancel">Cancel</localize> <localize key="general_cancel">Cancel</localize>
</a> </a>
<button class="btn btn-primary" ng-click="copy()" ng-disabled="busy || !target"> <button class="btn btn-primary" ng-click="copy()" ng-disabled="busy">
<localize key="actions_copy">Copy</localize> <localize key="actions_copy">Copy</localize>
</button> </button>
</div> </div>
@@ -50,10 +50,8 @@
val-form-manager> val-form-manager>
<div ng-show="error"> <div ng-show="error">
<div class="alert alert-error"> <h5 class="text-error">{{error.errorMsg}}</h5>
<div><strong>{{error.errorMsg}}</strong></div> <p class="text-error">{{error.data.message}}</p>
<div>{{error.data.message}}</div>
</div>
</div> </div>
<umb-control-group label="Enter a folder name" hide-label="false"> <umb-control-group label="Enter a folder name" hide-label="false">
@@ -76,25 +74,23 @@
val-form-manager> val-form-manager>
<div ng-show="error"> <div ng-show="error">
<div class="alert alert-error"> <h5 class="text-error">{{error.errorMsg}}</h5>
<div><strong>{{error.errorMsg}}</strong></div> <p class="text-error">{{error.data.message}}</p>
<div>{{error.data.message}}</div>
</div>
</div> </div>
<umb-control-group label="Name of the Parent Document Type" hide-label="false"> <umb-control-group label="Name of the Parent Document Type" hide-label="false">
<input type="text" name="collectionName" ng-model="model.collectionName" class="umb-textstring textstring input-block-level" umb-auto-focus required /> <input type="text" name="collectionName" ng-model="model.collectionName" class="umb-textstring textstring input-block-level" umb-auto-focus required />
<span ng-if="model.disableTemplates === false"> <span ng-if="model.disableTemplates === false">
<input id="collectionCreateTemplate" name="collectionCreateTemplate" type="checkbox" ng-model="model.collectionCreateTemplate" style="margin-top: 0;" /> <umb-toggle on-click="model.collectionCreateTemplate = !model.collectionCreateTemplate" checked="model.collectionCreateTemplate"></umb-toggle>
<label for="collectionCreateTemplate" style="margin-bottom: 0; padding-left: 2px;">Create template for the Parent Document Type</label> <label>Create template for the Parent Document Type</label>
</span> </span>
</umb-control-group> </umb-control-group>
<umb-control-group label="Name of the Item Document Type" hide-label="false"> <umb-control-group label="Name of the Item Document Type" hide-label="false">
<input type="text" name="collectionItemName" ng-model="model.collectionItemName" class="umb-textstring textstring input-block-level" required /> <input type="text" name="collectionItemName" ng-model="model.collectionItemName" class="umb-textstring textstring input-block-level" required />
<span ng-if="model.disableTemplates === false"> <span ng-if="model.disableTemplates === false">
<input id="collectionItemCreateTemplate" name="collectionItemCreateTemplate" type="checkbox" ng-model="model.collectionItemCreateTemplate" style="margin-top: 0;" /> <umb-toggle on-click="model.collectionItemCreateTemplate = !model.collectionItemCreateTemplate" checked="model.collectionItemCreateTemplate"></umb-toggle>
<label for="collectionItemCreateTemplate" style="margin-bottom: 0; padding-left: 2px;">Create template for the Item Document Type</label> <label>Create template for the Item Document Type</label>
</span> </span>
</umb-control-group> </umb-control-group>
@@ -9,7 +9,7 @@
(function () { (function () {
"use strict"; "use strict";
function DocumentTypesEditController($scope, $routeParams, $injector, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService, overlayHelper, eventsService, angularHelper) { function DocumentTypesEditController($scope, $routeParams, $injector, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService, overlayHelper, eventsService) {
var vm = this; var vm = this;
var localizeSaving = localizationService.localize("general_saving"); var localizeSaving = localizationService.localize("general_saving");
@@ -375,15 +375,6 @@
eventsService.unsubscribe(evts[e]); eventsService.unsubscribe(evts[e]);
} }
}); });
// #3368 - changes on the other "buttons" do not register on the current form, so we manually have to flag the form as dirty
$scope.$watch("vm.contentType.allowedContentTypes.length + vm.contentType.allowAsRoot + vm.contentType.allowedTemplates.length + vm.contentType.isContainer", function (newVal, oldVal) {
if (oldVal === undefined) {
// still initializing, ignore
return;
}
angularHelper.getCurrentForm($scope).$setDirty();
});
} }
angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.EditController", DocumentTypesEditController); angular.module("umbraco").controller("Umbraco.Editors.DocumentTypes.EditController", DocumentTypesEditController);

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