Merge remote-tracking branch 'origin/dev-v7' into temp8

# Conflicts:
#	src/SolutionInfo.cs
#	src/Umbraco.Core/Configuration/GlobalSettings.cs
#	src/Umbraco.Core/Configuration/UmbracoConfig.cs
#	src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs
#	src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs
#	src/Umbraco.Core/Models/IContentTypeBase.cs
#	src/Umbraco.Core/Persistence/DatabaseSchemaHelper.cs
#	src/Umbraco.Core/Persistence/Migrations/Initial/BaseDataCreation.cs
#	src/Umbraco.Core/Persistence/Repositories/ContentRepository.cs
#	src/Umbraco.Core/Persistence/Repositories/Implement/MemberGroupRepository.cs
#	src/Umbraco.Core/Services/ContentService.cs
#	src/Umbraco.Tests/Web/TemplateUtilitiesTests.cs
#	src/Umbraco.Tests/packages.config
#	src/Umbraco.Web.UI.Client/bower.json
#	src/Umbraco.Web.UI.Client/lib/umbraco/LegacyUmbClientMgr.js
#	src/Umbraco.Web.UI.Client/src/common/directives/components/imaging/umbimagegravity.directive.js
#	src/Umbraco.Web.UI.Client/src/common/resources/media.resource.js
#	src/Umbraco.Web.UI.Client/src/common/services/navigation.service.js
#	src/Umbraco.Web.UI.Client/src/common/services/umbrequesthelper.service.js
#	src/Umbraco.Web.UI.Client/src/less/belle.less
#	src/Umbraco.Web.UI.Client/src/less/buttons.less
#	src/Umbraco.Web.UI.Client/src/less/components/umb-tabs.less
#	src/Umbraco.Web.UI.Client/src/less/forms.less
#	src/Umbraco.Web.UI.Client/src/less/hacks.less
#	src/Umbraco.Web.UI.Client/src/less/modals.less
#	src/Umbraco.Web.UI.Client/src/less/panel.less
#	src/Umbraco.Web.UI.Client/src/views/common/dialogs/approvedcolorpicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/common/dialogs/login.controller.js
#	src/Umbraco.Web.UI.Client/src/views/common/infiniteeditors/mediapicker/mediapicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/common/overlays/mediaPicker/mediapicker.html
#	src/Umbraco.Web.UI.Client/src/views/components/imaging/umb-image-gravity.html
#	src/Umbraco.Web.UI.Client/src/views/dashboard/media/mediadashboardvideos.html
#	src/Umbraco.Web.UI.Client/src/views/dashboard/members/membersdashboardvideos.html
#	src/Umbraco.Web.UI.Client/src/views/dashboard/settings/settingsdashboardvideos.html
#	src/Umbraco.Web.UI.Client/src/views/documenttypes/edit.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/boolean/boolean.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/colorpicker/colorpicker.prevalues.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/datepicker/datepicker.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/grid/grid.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/imagecropper/imagecropper.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.controller.js
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/multipletextbox/multipletextbox.html
#	src/Umbraco.Web.UI.Client/src/views/propertyeditors/rte/rte.html
#	src/Umbraco.Web.UI.Client/src/views/users/user.html
#	src/Umbraco.Web.UI.Client/src/views/users/views/user/details.html
#	src/Umbraco.Web.UI/Umbraco/Views/Preview/Index.cshtml
#	src/Umbraco.Web.UI/Umbraco/config/lang/da.xml
#	src/Umbraco.Web.UI/Umbraco/dialogs/protectPage.aspx
#	src/Umbraco.Web.UI/Umbraco/dialogs/rollBack.aspx
#	src/Umbraco.Web.UI/config/EmbeddedMedia.Release.config
#	src/Umbraco.Web.UI/config/EmbeddedMedia.config
#	src/Umbraco.Web.UI/umbraco/config/lang/en.xml
#	src/Umbraco.Web.UI/umbraco/config/lang/en_us.xml
#	src/Umbraco.Web.UI/umbraco/dialogs/sort.aspx
#	src/Umbraco.Web.UI/umbraco_client/Dialogs/PublishDialog.js
#	src/Umbraco.Web.UI/web.Template.config
#	src/Umbraco.Web/Editors/ContentController.cs
#	src/Umbraco.Web/HealthCheck/Checks/DataIntegrity/DatabaseSchemaValidationHealthCheck.cs
#	src/Umbraco.Web/Models/Mapping/MemberModelMapper.cs
#	src/Umbraco.Web/Models/Trees/TreeNodeExtensions.cs
#	src/Umbraco.Web/PropertyEditors/ColorListPreValueEditor.cs
#	src/Umbraco.Web/PropertyEditors/EmailAddressPropertyEditor.cs
#	src/Umbraco.Web/PropertyEditors/ImageCropperPropertyValueEditor.cs
#	src/Umbraco.Web/PropertyEditors/TrueFalsePropertyEditor.cs
#	src/Umbraco.Web/PublishedCache/XmlPublishedCache/XmlPublishedContent.cs
#	src/Umbraco.Web/Routing/ContentFinderByUrlAlias.cs
#	src/Umbraco.Web/Templates/TemplateUtilities.cs
#	src/Umbraco.Web/UmbracoHelper.cs
#	src/Umbraco.Web/WebServices/SaveFileController.cs
#	src/Umbraco.Web/umbraco.presentation/umbraco/Trees/FileSystemTree.cs
#	src/Umbraco.Web/umbraco.presentation/umbraco/controls/dualSelectBox.cs
#	src/Umbraco.Web/umbraco.presentation/umbraco/dashboard/FeedProxy.aspx.cs
#	src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/protectPage.aspx.cs
#	src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/rollBack.aspx
#	src/Umbraco.Web/umbraco.presentation/umbraco/dialogs/rollBack.aspx.cs
#	src/Umbraco.Web/umbraco.presentation/umbraco/settings/stylesheet/editstylesheet.aspx.cs
This commit is contained in:
Shannon
2018-10-22 16:03:01 +11:00
107 changed files with 1072 additions and 598 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ Assume positive intent and try to understand before being understood.
Treat others as you would like to be treated.
This also goes for treating the HQ with respect. For example: dont promote products on [our.umbraco.org](https://our.umbraco.org) that directly compete with our commercial offerings which enables us to work for a sustainable Umbraco.
This also goes for treating the HQ with respect. For example: dont promote products on [our.umbraco.com](https://our.umbraco.com) that directly compete with our commercial offerings which enables us to work for a sustainable Umbraco.
## Open
+1 -1
View File
@@ -82,7 +82,7 @@ These wonderful volunteers will provide you with a first reply to your PR, revie
You can get in touch with [the PR team](#the-pr-team) in multiple ways, we love open conversations and we are a friendly bunch. No question you have is stupid. Any questions you have usually helps out multiple people with the same question. Ask away:
- If there's an existing issue on the issue tracker then that's a good place to leave questions and discuss how to start or move forward
- Unsure where to start? Did something not work as expected? Try leaving a note in the ["Contributing to Umbraco"](https://our.umbraco.org/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
+6 -8
View File
@@ -31,7 +31,7 @@ Before creating bug reports, please check [this list](#before-submitting-a-bug-r
##### Before Submitting A Bug Report
* Most importantly, check **if you can reproduce the problem** in the [latest version of Umbraco](https://our.umbraco.org/download/). We might have already fixed your particular problem.
* Most importantly, check **if you can reproduce the problem** in the [latest version of Umbraco](https://our.umbraco.com/download/). We might have already fixed your particular problem.
* It also helps tremendously to check if the issue you're experiencing is present in **a clean install** of the Umbraco version you're currently using. Custom code can have side-effects that don't occur in a clean install.
* **Use the Google**. Whatever you're experiencing, Google it plus "Umbraco" - usually you can get some pretty good hints from the search results, including open issues and further troubleshooting hints.
* If you do find and existing issue has **and the issue is still open**, add a comment to the existing issue if you have additional information. If you have the same problem and no new info to add, just "star" the issue.
@@ -65,13 +65,11 @@ Most of the suggestions in the [reporting bugs](#reporting-bugs) section also co
Some additional hints that may be helpful:
* **Include screenshots and animated GIFs** which help you demonstrate the steps or point out the part of Umbraco which the suggestion is related to.
* **Explain why this enhancement would be useful to most Umbraco users** and isn't something that can or should be implemented as a [community package](https://our.umbraco.org/projects/).
* **Explain why this enhancement would be useful to most Umbraco users** and isn't something that can or should be implemented as a [community package](https://our.umbraco.com/projects/).
### Your First Code Contribution
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` and issues](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.
Unsure where to begin contributing to Umbraco? You can start by looking through [these `Up for grabs` and issues](https://issues.umbraco.org/issues?q=&project=U4&tagValue=upforgrabs&release=&issueType=&search=search) or on the [new issue tracker](https://github.com/umbraco/Umbraco-CMS/issues?q=is%3Aopen+is%3Aissue+label%3Acommunity%2Fup-for-grabs).
### Pull Requests
@@ -80,7 +78,7 @@ The most successful pull requests usually look a like this:
* Fill in the required template
* Include screenshots and animated GIFs in your pull request whenever possible.
* Unit tests, while optional are awesome, thank you!
* New code is commented with documentation from which [the reference documentation](https://our.umbraco.org/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.
@@ -116,8 +114,8 @@ There's two big areas that you should know about:
To find the general areas of something you're looking to fix or improve, have a look at the following two parts of the API documentation.
* [The AngularJS based backoffice files](https://our.umbraco.org/apidocs/ui/#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
* [The rest](https://our.umbraco.org/apidocs/csharp/)
* [The AngularJS based backoffice files](https://our.umbraco.com/apidocs/ui/#/api) (to be found in `src\Umbraco.Web.UI.Client\src`)
* [The rest](https://our.umbraco.com/apidocs/csharp/)
### What branch should I target for my contributions?
+1 -1
View File
@@ -1,6 +1,6 @@
# The MIT License (MIT) #
Copyright (c) 2013 Umbraco
Copyright (c) 2013-present Umbraco
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
@@ -8,7 +8,7 @@
<meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
<meta name="generator" content="docfx {{_docfxVersion}}">
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
<link rel="icon" type="image/png" href="https://our.umbraco.org/assets/images/app-icons/favicon.png">
<link rel="icon" type="image/png" href="https://our.umbraco.com/assets/images/app-icons/favicon.png">
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.css">
<link rel="stylesheet" href="{{_rel}}styles/docfx.css">
<link rel="stylesheet" href="{{_rel}}styles/main.css">
+1 -1
View File
@@ -63,7 +63,7 @@ a:focus {
}
.navbar-header .navbar-brand {
background: url(https://our.umbraco.org/assets/images/logo.svg) left center no-repeat;
background: url(https://our.umbraco.com/assets/images/logo.svg) left center no-repeat;
background-size: 40px auto;
width:50px;
}
+1 -1
View File
@@ -2,7 +2,7 @@
using System.Resources;
[assembly: AssemblyCompany("Umbraco")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2017")]
[assembly: AssemblyCopyright("Copyright © Umbraco 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
@@ -61,7 +61,8 @@ namespace Umbraco.Core.Configuration
var config = WebConfigurationManager.OpenWebConfiguration(appPath);
var settings = (MailSettingsSectionGroup)config.GetSectionGroup("system.net/mailSettings");
if (settings == null || settings.Smtp == null) return false;
// note: "noreply@example.com" is/was the sample SMTP from email - we'll regard that as "not configured"
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)
return true;
if (settings.Smtp.Network != null && string.IsNullOrEmpty(settings.Smtp.Network.Host) == false)
@@ -193,4 +193,4 @@ namespace Umbraco.Core.Configuration
//TODO: Add other configurations here !
}
}
}
@@ -81,12 +81,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
[ConfigurationProperty("loginBackgroundImage")]
internal InnerTextConfigurationElement<string> LoginBackgroundImage => GetOptionalTextElement("loginBackgroundImage", string.Empty);
[ConfigurationProperty("StripUdiAttributes")]
internal InnerTextConfigurationElement<bool> StripUdiAttributes
{
get { return GetOptionalTextElement("StripUdiAttributes", true); }
}
string IContentSection.NotificationEmailAddress => Notifications.NotificationEmailAddress;
@@ -142,7 +136,6 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
bool IContentSection.EnableInheritedMediaTypes => EnableInheritedMediaTypes;
bool IContentSection.StripUdiAttributes => StripUdiAttributes;
string IContentSection.LoginBackgroundImage => LoginBackgroundImage;
}
@@ -66,7 +66,5 @@ namespace Umbraco.Core.Configuration.UmbracoSettings
bool EnableInheritedMediaTypes { get; }
string LoginBackgroundImage { get; }
bool StripUdiAttributes { get; }
}
}
}
+6 -1
View File
@@ -21,7 +21,12 @@ namespace Umbraco.Core.Models
string Description { get; set; }
/// <summary>
/// Gets or Sets the Icon for the ContentType
/// Gets or sets the icon for the content type. The value is a CSS class name representing
/// 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>
string Icon { get; set; }
@@ -262,7 +262,7 @@ namespace Umbraco.Core.Persistence.Repositories.Implement
var nonAssignedRoles = roleNames.Except(assignedRoles, StringComparer.CurrentCultureIgnoreCase);
foreach (var toAssign in nonAssignedRoles)
{
var groupId = rolesForNames.First(x => x.Text == toAssign).NodeId;
var groupId = rolesForNames.First(x => x.Text.InvariantEquals(toAssign)).NodeId;
Database.Insert(new Member2MemberGroupDto { Member = mId, MemberGroup = groupId });
}
}
@@ -292,6 +292,29 @@ namespace Umbraco.Tests.Services
Assert.AreEqual(2, membersInRole.Count());
}
[Test]
public void Associate_Members_To_Roles_With_Member_Id_Casing()
{
ServiceContext.MemberService.AddRole("MyTestRole1");
IMemberType memberType = MockedContentTypes.CreateSimpleMemberType();
ServiceContext.MemberTypeService.Save(memberType);
var member1 = MockedMember.CreateSimpleMember(memberType, "test1", "test1@test.com", "pass", "test1");
ServiceContext.MemberService.Save(member1);
var member2 = MockedMember.CreateSimpleMember(memberType, "test2", "test2@test.com", "pass", "test2");
ServiceContext.MemberService.Save(member2);
// temp make sure they exist
Assert.IsNotNull(ServiceContext.MemberService.GetById(member1.Id));
Assert.IsNotNull(ServiceContext.MemberService.GetById(member2.Id));
ServiceContext.MemberService.AssignRoles(new[] { member1.Id, member2.Id }, new[] { "mytestrole1" });
var membersInRole = ServiceContext.MemberService.GetMembersInRole("MyTestRole1");
Assert.AreEqual(2, membersInRole.Count());
}
[Test]
public void Associate_Members_To_Roles_With_Member_Username()
{
@@ -1,8 +1,8 @@
using System;
using System.Globalization;
using System.Linq;
using System.Web;
using LightInject;
using HtmlAgilityPack;
using Moq;
using NUnit.Framework;
using Umbraco.Core;
@@ -12,6 +12,7 @@ using Umbraco.Core.Configuration.UmbracoSettings;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.Models.PublishedContent;
using Umbraco.Core.Services;
using Umbraco.Tests.TestHelpers;
using Umbraco.Tests.TestHelpers.Stubs;
using Umbraco.Tests.Testing.Objects.Accessors;
@@ -63,14 +64,6 @@ namespace Umbraco.Tests.Web
[TestCase("hello href=\"{localLink:umb://document-type/9931BDE0AAC34BABB838909A7B47570E}\" world ", "hello href=\"/my-test-url\" world ")]
//this one has an invalid char so won't match
[TestCase("hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ", "hello href=\"{localLink:umb^://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\" world ")]
// with a-tag with data-udi attribute, that needs to be stripped
[TestCase("hello <a data-udi=\"umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570\" href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\"> world</a> ", "hello <a href=\"/my-test-url\"> world</a> ")]
// with a-tag with data-udi attribute spelled wrong, so don't need stripping
[TestCase("hello <a data-uid=\"umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570\" href=\"{localLink:umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570E}\"> world</a> ", "hello <a data-uid=\"umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570\" href=\"/my-test-url\"> world</a> ")]
// with a img-tag with data-udi id, that needs to be strippde
[TestCase("hello <img data-udi=\"umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570\" src=\"imageofcats.jpg\"> world ", "hello <img src=\"imageofcats.jpg\"> world ")]
// with a img-tag with data-udi id spelled wrong, so don't need stripping
[TestCase("hello <img data-uid=\"umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570\" src=\"imageofcats.jpg\"> world ", "hello <img data-uid=\"umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570\" src=\"imageofcats.jpg\"> world ")]
public void ParseLocalLinks(string input, string result)
{
var serviceCtxMock = new TestObjects(null).GetServiceContextMock();
@@ -111,7 +104,7 @@ namespace Umbraco.Tests.Web
//setup a quick mock of the WebRouting section
Mock.Of<IUmbracoSettingsSection>(section => section.WebRouting == Mock.Of<IWebRoutingSection>(routingSection => routingSection.UrlProviderMode == "AutoLegacy")),
//pass in the custom url provider
new[] { testUrlProvider.Object },
new[]{ testUrlProvider.Object },
globalSettings,
new TestVariationContextAccessor(),
true))
@@ -121,27 +114,5 @@ namespace Umbraco.Tests.Web
Assert.AreEqual(result, output);
}
}
[Test]
public void StripDataUdiAttributesUsingSrtringOnLinks()
{
var input = "hello <a data-udi=\"umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570\" href=\"/my-test-url\"> world</a> ";
var expected = "hello <a href=\"/my-test-url\"> world</a> ";
var result = TemplateUtilities.StripUdiDataAttributes(input);
Assert.AreEqual(expected, result);
}
[Test]
public void StripDataUdiAttributesUsingStringOnImages()
{
var input = "hello <img data-udi=\"umb://document-type/9931BDE0-AAC3-4BAB-B838-909A7B47570\" src=\"imageofcats.jpg\"> world ";
var expected = "hello <img src=\"imageofcats.jpg\"> world ";
var result = TemplateUtilities.StripUdiDataAttributes(input);
Assert.AreEqual(expected, result);
}
}
}
}
@@ -146,7 +146,8 @@ Umbraco.Sys.registerNamespace("Umbraco.Application");
sourceUrl: currentMenuNode.childNodesUrl,
updateDefinition: function() {
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
Object.defineProperty(legacyNode, "menu", {
@@ -1,4 +1,17 @@
(function() {
/**
@ngdoc directive
@name umbraco.directives.directive:umbTourStep
@restrict E
@scope
@description
<b>Added in Umbraco 7.8</b>. The tour step component is a component that can be used in custom views for tour steps.
@param {callback} onClose The callback which should be performened when the close button of the tour step is clicked
@param {boolean=} hideClose A boolean indicating if the close button needs to be shown
**/
(function () {
'use strict';
function TourStepDirective() {
@@ -1,4 +1,17 @@
(function() {
/**
@ngdoc directive
@name umbraco.directives.directive:umbTourStepContent
@restrict E
@scope
@description
<b>Added in Umbraco 7.8</b>. The tour step content component is a component that can be used in custom views for tour steps.
It's meant to be used in the umb-tour-step directive.
All markup in the body of the directive will be shown after the content attribute
@param {string} content The content that needs to be shown
**/
(function () {
'use strict';
function TourStepContentDirective() {
@@ -1,4 +1,18 @@
(function() {
/**
@ngdoc directive
@name umbraco.directives.directive:umbTourStepCounter
@restrict E
@scope
@description
<b>Added in Umbraco 7.8</b>. The tour step counter component is a component that can be used in custom views for tour steps.
It's meant to be used in the umb-tour-step-footer directive. It will show the progress you have made in a tour eg. step 2/12
@param {int} currentStep The current step the tour is on
@param {int} totalSteps The current step the tour is on
**/
(function () {
'use strict';
function TourStepCounterDirective() {
@@ -1,4 +1,16 @@
(function() {
/**
@ngdoc directive
@name umbraco.directives.directive:umbTourStepFooter
@restrict E
@scope
@description
<b>Added in Umbraco 7.8</b>. The tour step footer component is a component that can be used in custom views for tour steps. It's meant to be used in the umb-tour-step directive.
All markup in the body of the directive will be shown as the footer of the tour step
**/
(function () {
'use strict';
function TourStepFooterDirective() {
@@ -1,4 +1,16 @@
(function() {
/**
@ngdoc directive
@name umbraco.directives.directive:umbTourStepHeader
@restrict E
@scope
@description
<b>Added in Umbraco 7.8</b>. The tour step header component is a component that can be used in custom views for tour steps. It's meant to be used in the umb-tour-step directive.
@param {string} title The title that needs to be shown
**/
(function () {
'use strict';
function TourStepHeaderDirective() {
@@ -48,7 +48,7 @@ the directive will use {@link umbraco.directives.directive:umbLockedField umbLoc
**/
angular.module("umbraco.directives")
.directive('umbGenerateAlias', function ($timeout, entityResource) {
.directive('umbGenerateAlias', function ($timeout, entityResource, localizationService) {
return {
restrict: 'E',
templateUrl: 'views/components/umb-generate-alias.html',
@@ -67,7 +67,21 @@ angular.module("umbraco.directives")
var updateAlias = false;
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) {
@@ -78,7 +92,7 @@ angular.module("umbraco.directives")
if( value !== undefined && value !== "" && value !== null) {
scope.alias = "";
scope.placeholderText = "Generating Alias...";
scope.placeholderText = scope.labels.busy;
generateAliasTimeout = $timeout(function () {
updateAlias = true;
@@ -92,7 +106,7 @@ angular.module("umbraco.directives")
} else {
updateAlias = true;
scope.alias = "";
scope.placeholderText = "Enter alias...";
scope.placeholderText = scope.labels.idle;
}
}
@@ -125,13 +125,19 @@ Use this directive to generate a thumbnail grid of media items.
i--;
}
if (scope.includeSubFolders !== 'true') {
if (item.parentId !== parseInt(scope.currentFolderId)) {
scope.items.splice(i, 1);
i--;
// If subfolder search is not enabled remove the media items that's not needed
// Make sure that includeSubFolder is not undefined since the directive is used
// in contexts where it should not be used. Currently only used when we trigger
// a media picker
if(scope.includeSubFolders !== undefined){
if (scope.includeSubFolders !== 'true') {
if (item.parentId !== parseInt(scope.currentFolderId)) {
scope.items.splice(i, 1);
i--;
}
}
}
}
@@ -152,7 +158,7 @@ Use this directive to generate a thumbnail grid of media items.
}
if (!item.isFolder) {
// handle entity
if(item.image) {
item.thumbnail = mediaHelper.resolveFileFromEntity(item, true);
@@ -161,7 +167,7 @@ Use this directive to generate a thumbnail grid of media items.
} else {
item.thumbnail = mediaHelper.resolveFile(item, true);
item.image = mediaHelper.resolveFile(item, false);
var fileProp = _.find(item.properties, function (v) {
return (v.alias === "umbracoFile");
});
@@ -109,11 +109,23 @@ function mediaResource($q, $http, umbDataFormatter, umbRequestHelper) {
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("mediaApiBaseUrl", "PostMove"),
{
parentId: args.parentId,
id: args.id
{
parentId: args.parentId,
id: args.id
}, {responseType: 'text'}),
'Failed to move media');
{
error: function(data){
var errorMsg = 'Failed to move media';
if(data.parentId === data.id){
errorMsg = 'Media can\'t be moved into itself';
}
return {
errorMsg: errorMsg
};
}
});
},
@@ -646,10 +646,10 @@ function navigationService($rootScope, $route, $routeParams, $log, $location, $q
*/
hideDialog: function (showMenu) {
setMode("default");
if(showMenu){
if (showMenu) {
this.showMenu({ skipDefault: true, node: appState.getMenuState("currentNode") });
} else {
setMode("default");
}
},
/**
@@ -126,12 +126,21 @@ function umbRequestHelper($http, $q, umbDataFormatter, angularHelper, dialogServ
/** The default error callback used if one is not supplied in the opts */
function defaultError(data, status, headers, config) {
return {
var err = {
//NOTE: the default error message here should never be used based on the above docs!
errorMsg: (angular.isString(opts) ? opts : 'An error occurred!'),
data: data,
status: status
};
// if "opts" is a promise, we set "err.errorMsg" to be that promise
if (typeof(opts) == "object" && typeof(opts.then) == "function") {
err.errorMsg = opts;
}
return err;
}
//create the callbacs based on whats been passed in.
@@ -66,6 +66,7 @@
// Belle styles
@import "buttons.less";
@import "forms.less";
@import "legacydialog.less";
@import "modals.less";
@import "panel.less";
@import "sections.less";
@@ -159,6 +160,7 @@
@import "components/umb-mini-editor.less";
@import "components/users/umb-user-cards.less";
@import "components/users/umb-user-details.less";
@import "components/users/umb-user-group-picker-list.less";
@import "components/users/umb-user-group-preview.less";
@import "components/users/umb-user-preview.less";
@@ -63,9 +63,6 @@
.btn-group>.btn+.dropdown-toggle {
box-shadow: none;
-webkit-box-shadow:none;
}
.btn-group .btn.dropdown-toggle {
border-left-width: 1px;
border-left-style: solid;
border-color: rgba(0,0,0,0.09);
@@ -81,9 +81,9 @@ a, a:hover{
.wait {
display: block;
height: 280px;
height: 100%;
width: 100%;
background: center center url(../img/loader.gif) no-repeat;
background:#fff center center url(../img/loader.gif) no-repeat;
}
/****************************/
@@ -159,6 +159,7 @@
margin-bottom: 1px;
border-radius: 0;
border-bottom: 1px solid @gray-9;
padding: 10px;
}
.umb-help-list-item:last-child {
@@ -193,6 +194,7 @@
.umb-help-list-item__title {
font-size: 14px;
display: block;
margin-left: 26px;
}
.umb-help-list-item__description {
@@ -205,6 +207,7 @@
margin-right: 8px;
color: @gray-4;
font-size: 18px;
float: left;
}
.umb-help-list-item__open-icon {
@@ -219,4 +222,4 @@
[data-element*="tour-"].umb-help-list-item:hover .umb-help-list-item__title {
text-decoration:none;
}
}
@@ -10,6 +10,10 @@
}
}
.umb-toggle:focus .umb-toggle__toggle{
box-shadow: 0 1px 3px fade(@black, 12%), 0 1px 2px fade(@black, 24%);
}
.umb-toggle__handler {
position: absolute;
top: 0;
@@ -30,6 +34,7 @@
background: @gray-8;
border-radius: 90px;
position: relative;
transition: box-shadow .3s;
}
.umb-toggle--checked .umb-toggle__toggle {
@@ -43,7 +48,6 @@
/* Labels */
.umb-toggle__label {
font-size: 12px;
color: @gray-2;
}
@@ -101,11 +101,13 @@
}
.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 {
flex: 0 0 33.33%;
max-width:33.33%;
}
.umb-card-grid .umb-card-grid-item {
@@ -3,8 +3,8 @@
flex-flow: row wrap;
.umb-color-box {
border: none;
color: white;
border: 1px solid @gray-8;
color: @white;
cursor: pointer;
padding: 1px;
text-align: center;
@@ -19,7 +19,7 @@
justify-content: center;
&:hover, &:focus {
box-shadow: 0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24);
box-shadow: 0 1px 3px fade(@black, 12%), 0 1px 2px fade(@black, 24%);
}
&.umb-color-box--m {
@@ -50,7 +50,7 @@
padding-top: 10px;
.umb-color-box__label {
background: #fff;
background: @white;
font-size: 14px;
display: flex;
flex-flow: column wrap;
@@ -63,7 +63,8 @@
margin-right: -1px;
text-indent: 0;
text-align: left;
border: 1px solid @gray-8;
border-top: 1px solid @gray-8;
border-bottom: 1px solid @gray-8;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
overflow: hidden;
@@ -1,4 +1,17 @@
.umb-multiple-textbox .textbox-wrapper {
.umb-multiple-textbox{
&__confirm{
position: relative;
&-action{
margin: 0;
padding: 2px;
background: transparent;
border: 0 none;
}
}
}
.umb-multiple-textbox .textbox-wrapper {
align-items: center;
margin-bottom: 15px;
}
@@ -7,7 +20,7 @@
margin-bottom: 0;
}
.umb-multiple-textbox .textbox-wrapper i {
.umb-multiple-textbox .textbox-wrapper i:not(.icon-delete, .icon-check) {
margin-right: 5px;
}
@@ -1,5 +1,6 @@
.umb-nested-content {
text-align: center;
position: relative;
}
.umb-nested-content--not-supported {
@@ -208,11 +209,25 @@
width: 99%;
}
.usky-grid.umb-nested-content__node-type-picker .cell-tools-menu {
position: relative;
transform: translate(-50%, -25%);
}
.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
// the attribute selector ensures the change only applies to the linkpicker overlay
@@ -0,0 +1,122 @@
.umb-user-details-avtar {
margin-bottom: 20px;
padding-bottom: 20px;
border-bottom: 1px solid #d8d7d9;
}
div.umb-user-details-actions > div {
margin-bottom: 20px;
}
.umb-user-details-actions .umb-button {
margin-bottom: 20px;
}
.umb-user-details-view-title {
font-size: 20px;
font-weight: bold;
color: @black;
margin-bottom: 30px;
}
.umb-user-details-view-wrapper {
padding: 20px 60px;
}
@media (max-width: 768px) {
.umb-user-details-view-wrapper {
padding: 0;
}
}
.umb-user-details-section {
margin-bottom: 40px;
}
.umb-user-details-details {
display: flex;
}
a.umb-user-details-details__back-link {
font-weight: bold;
color: @black;
}
.umb-user-details-details__back-link:hover {
color: @gray-4;
text-decoration: none;
}
@sidebarwidth: 350px; // Width of sidebar. Ugly hack because of old version of Less
.umb-user-details-details__main-content {
flex: 1 1 auto;
margin-right: 30px;
width: calc(~'100%' - ~'@{sidebarwidth}' - ~'30px'); // Make sure that the main content area doesn't gets affected by inline styling
}
.umb-user-details-details__main-content .umb-node-preview-add {
max-width: 100%;
}
.umb-user-details-details__sidebar {
flex: 0 0 @sidebarwidth;
}
@media (max-width: 768px) {
.umb-user-details-details {
flex-direction: column;
}
.umb-user-details-details__main-content {
flex: 1 1 auto;
width: 100%;
margin-bottom: 30px;
margin-right: 0;
}
.umb-user-details-details__sidebar {
flex: 1 1 auto;
width: 100%;
}
}
.umb-user-details-details__section {
background: @gray-10;
padding: 20px;
margin-bottom: 20px;
border-radius: 3px;
border: 1px solid @gray-8;
}
.umb-user-details-details__section-title {
font-size: 17px;
font-weight: bold;
color: @black;
margin-top: 0;
margin-bottom: 15px;
}
.umb-user-details-details__section-description {
font-size: 12px;
line-height: 1.6em;
margin-bottom: 15px;
}
.umb-user-details-details__information-item {
margin-bottom: 10px;
font-size: 13px;
}
.umb-user-details-details__information-item-label {
color: @black;
font-weight: bold;
}
.umb-user-details-details__information-item-content {
word-break: break-word;
}
@@ -580,7 +580,7 @@ div.help {
text-align: center;
text-shadow: 0 1px 0 @white;
background-color: @gray-10;
border: 1px solid @gray-8;
border: 1px solid @purple-l3;
}
.add-on,
.btn,
@@ -202,7 +202,7 @@ pre {
//font-size: @baseFontSize - 1; // 14px to 13px
color: @gray-2;
line-height: @baseLineHeight;
white-space: pre-line; // 1
white-space: pre-wrap; // 1
overflow-x: auto; // 1
background-color: @gray-10;
border: 1px solid @gray-8;
@@ -222,4 +222,4 @@ pre {
background-color: transparent;
border: 0;
}
}
}
@@ -0,0 +1,58 @@
.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;
}
@@ -52,6 +52,16 @@
-webkit-flex: 1;
-ms-flex: 1;
flex: 1;
&__toggle{
margin: 10px 0;
label{
margin-right: 10px;
position: relative;
top: -2px;
}
}
}
.upload-button {
@@ -120,6 +120,13 @@ div.umb-codeeditor .umb-btn-toolbar {
//
// RTE
// --------------------------------------------------
.umb-rte {
position: relative;
.-loading {
position: absolute;
}
}
.mce-tinymce{border: 1px solid @gray-8 !important; border-radius: 0px !important;}
.mce-panel{background: @gray-10 !important; border-color: @gray-8 !important;}
.mce-btn-group, .mce-btn{border: none !important; background: none !important;}
@@ -142,11 +149,14 @@ div.umb-codeeditor .umb-btn-toolbar {
/* pre-value editor */
.control-group.color-picker-preval {
.thumbnail {
width: 36px;
width: 34px;
height: 34px;
min-width: auto;
border: none;
cursor: move;
border-radius: 3px;
margin-top: auto;
margin-bottom: auto;
}
.handle {
@@ -158,19 +168,19 @@ div.umb-codeeditor .umb-btn-toolbar {
div.color-picker-prediv {
display: inline-flex;
align-items: center;
max-width: 85%;
max-width: 100%;
flex: 1;
pre {
display: inline-flex;
font-family: monospace;
margin-right: 10px;
margin-left: 10px;
margin-left: 15px;
margin-right: 15px;
white-space: nowrap;
overflow: hidden;
margin-bottom: 0;
vertical-align: middle;
padding-top: 7px;
padding-bottom: 7px;
padding: 6px 10px;
background: #f7f7f7;
flex: 0 0 auto;
}
@@ -199,11 +209,11 @@ div.umb-codeeditor .umb-btn-toolbar {
label {
border: 1px solid #fff;
padding: 7px 10px;
padding: 6px 10px;
font-family: monospace;
border: 1px solid #dfdfe1;
background: #f7f7f7;
margin: 0 15px 0 0;
margin: 0 15px 0 3px;
border-radius: 3px;
}
}
@@ -310,7 +320,7 @@ div.umb-codeeditor .umb-btn-toolbar {
.umb-mediapicker .umb-sortable-thumbnails li {
flex-direction: column;
margin: 0 5px 5px 0;
margin: 0 0 5px 5px;
padding: 5px;
}
@@ -579,6 +589,16 @@ div.umb-codeeditor .umb-btn-toolbar {
}
}
.imagecropper .umb-cropper__container .button-drawer {
display: flex;
justify-content: flex-end;
padding: 10px;
button {
margin-left: 4px;
}
}
.umb-close-cropper {
position: absolute;
top: 3px;
@@ -843,7 +863,20 @@ div.umb-codeeditor .umb-btn-toolbar {
//
// Nested boolean (e.g. list view bulk action permissions)
// ---------------------=====-----------------------------
// -------------------------------------------------------
.umb-nested-boolean label {margin-bottom: 8px; float: left; width: 320px;}
.umb-nested-boolean label span {float: left; width: 80%;}
.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.allowPasswordReset;
$scope.allowPasswordReset = Umbraco.Sys.ServerVariables.umbracoSettings.canSendRequiredEmail && Umbraco.Sys.ServerVariables.umbracoSettings.allowPasswordReset;
$scope.showLogin = function () {
$scope.errorMsg = "";
@@ -151,7 +151,7 @@ angular.module("umbraco")
if (folder.id > 0) {
entityResource.getAncestors(folder.id, "media")
.then(function(anc) {
.then(function(anc) {
$scope.path = _.filter(anc,
function(f) {
return f.path.indexOf($scope.startNodeId) !== -1;
@@ -236,7 +236,10 @@ angular.module("umbraco")
$scope.onUploadComplete = function(files) {
$scope.gotoFolder($scope.currentFolder).then(function() {
if (files.length === 1 && $scope.model.selectedImages.length === 0) {
selectImage($scope.images[$scope.images.length - 1]);
var image = $scope.images[$scope.images.length - 1];
$scope.target = image;
$scope.target.url = mediaHelper.resolveFile(image);
selectImage(image);
}
});
};
@@ -305,6 +308,23 @@ angular.module("umbraco")
debounceSearchMedia();
};
/**
* 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
$scope.changeSearch();
// Toggle the showChilds option
if($scope.showChilds){
$scope.showChilds = false;
return;
}
$scope.showChilds = true;
}
$scope.changePagination = function(pageNumber) {
$scope.loading = true;
$scope.searchOptions.pageNumber = pageNumber;
@@ -1,4 +1,4 @@
<button ng-click="click()" type="button" class="umb-toggle dib" ng-class="{'umb-toggle--checked': checked}">
<button ng-click="click()" type="button" class="umb-toggle" ng-class="{'umb-toggle--checked': checked}">
<span ng-if="!labelPosition && showLabels === 'true' || labelPosition === 'left' && showLabels === 'true'">
<span ng-if="!checked" class="umb-toggle__label umb-toggle__label--left">{{ displayLabelOff }}</span>
@@ -6,9 +6,9 @@
</span>
<div class="umb-toggle__toggle">
<i ng-show="checked && !hideIcons" class="umb-toggle__icon umb-toggle__icon--left icon-check"></i>
<i ng-show="checked && hideIcons !== 'true'" class="umb-toggle__icon umb-toggle__icon--left icon-check"></i>
<div class="umb-toggle__handler shadow-depth-1"></div>
<i ng-show="!checked && !hideIcons" class="umb-toggle__icon umb-toggle__icon--right icon-wrong"></i>
<i ng-show="!checked && hideIcons !== 'true'" class="umb-toggle__icon umb-toggle__icon--right icon-wrong"></i>
</div>
<span ng-if="labelPosition === 'right' && showLabels === 'true'">
@@ -2,7 +2,7 @@
<div class="umb-package-details__main-content">
<umb-box data-element="node-info-urls">
<umb-box ng-if="node.urls" data-element="node-info-urls">
<umb-box-header title-key="general_links"></umb-box-header>
<umb-box-content class="block-form">
<ul class="nav nav-stacked" style="margin-bottom: 0;">
@@ -29,11 +29,11 @@
<umb-box-header title-key="general_general"></umb-box-header>
<umb-box-content class="block-form">
<umb-control-group data-element="node-info-create-date" label="@content_createDate">
<umb-control-group ng-if="node.id !== 0" data-element="node-info-create-date" label="@content_createDate">
{{node.createDateFormatted}} by {{ node.owner.name }}
</umb-control-group>
<umb-control-group data-element="node-info-update-date" label="@content_updateDate">
<umb-control-group ng-if="node.id !== 0" data-element="node-info-update-date" label="@content_updateDate">
{{node.updateDateFormatted}}
</umb-control-group>
@@ -47,7 +47,7 @@
</umb-node-preview>
</umb-control-group>
<umb-control-group data-element="node-info-id" label="Id">
<umb-control-group ng-if="node.id !== 0" data-element="node-info-id" label="Id">
<div>{{ node.id }}</div>
<small>{{ node.key }}</small>
</umb-control-group>
@@ -1,6 +1,6 @@
<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="{{color.label}}" 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 || 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)">
<div class="umb-color-box-inner">
<div class="check_circle" ng-if="color.value === selectedColor">
<i class="icon icon-check small"></i>
@@ -1,18 +1,20 @@
<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>
<h4>To get you started:</h4>
<div class="row-fluid"
ng-init="init('http://umbraco.tv/videos/implementor/chapterrss?sort=no')"
ng-init="init('https://umbraco.tv/videos/implementor/chapterrss?sort=no')"
ng-controller="Umbraco.Dashboard.StartupVideosController">
<ul class="thumbnails" >
<li class="span2" ng-repeat="video in videos">
<div class="thumbnail" style="margin-right: 20px">
<a target="_blank" href="{{video.link}}" title="{{video.title}}">
<img ng-src="{{video.thumbnail}}" alt="{{video.title}}">
</a>
</div>
</li>
</ul>
</div>
<div ng-show="videos.length">
<h4>To get you started:</h4>
<ul class="thumbnails" >
<li class="span2" ng-repeat="video in videos">
<div class="thumbnail" style="margin-right: 20px">
<a target="_blank" href="{{video.link}}" title="{{video.title}}">
<img ng-src="{{video.thumbnail}}" alt="{{video.title}}">
</a>
</div>
</li>
</ul>
</div>
</div>
@@ -1,5 +1,5 @@
<div class="row-fluid"
ng-init="init('https://www.umbraco.com/feeds/videos/members')"
ng-init="init('https://umbraco.com/feeds/videos/members')"
ng-controller="Umbraco.Dashboard.StartupVideosController">
<umb-box>
@@ -8,15 +8,17 @@
<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>
</umb-box-content>
</umb-box>
<ul class="thumbnails" >
<li class="span2" ng-repeat="video in videos">
<div class="thumbnail" style="margin-right: 20px">
<a target="_blank" href="{{video.link}}" title="{{video.title}}">
<img ng-src="{{video.thumbnail}}" alt="{{video.title}}">
</a>
</div>
</li>
</ul>
<div ng-show="videos.length">
<h4>To get you started:</h4>
<ul class="thumbnails" >
<li class="span2" ng-repeat="video in videos">
<div class="thumbnail" style="margin-right: 20px">
<a target="_blank" href="{{video.link}}" title="{{video.title}}">
<img ng-src="{{video.thumbnail}}" alt="{{video.title}}">
</a>
</div>
</li>
</ul>
</div>
</div>
@@ -1,18 +1,20 @@
<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>
<h4>To get you started:</h4>
<div class="row-fluid"
ng-init="init('http://umbraco.tv/videos/implementor/chapterrss?sort=no')"
ng-init="init('https://umbraco.tv/videos/implementor/chapterrss?sort=no')"
ng-controller="Umbraco.Dashboard.StartupVideosController">
<ul class="thumbnails" >
<li class="span2" ng-repeat="video in videos">
<div class="thumbnail" style="margin-right: 20px">
<a target="_blank" href="{{video.link}}" title="{{video.title}}">
<img ng-src="{{video.thumbnail}}" alt="{{video.title}}">
</a>
</div>
</li>
</ul>
</div>
<div ng-show="videos.length">
<h4>To get you started:</h4>
<ul class="thumbnails" >
<li class="span2" ng-repeat="video in videos">
<div class="thumbnail" style="margin-right: 20px">
<a target="_blank" href="{{video.link}}" title="{{video.title}}">
<img ng-src="{{video.thumbnail}}" alt="{{video.title}}">
</a>
</div>
</li>
</ul>
</div>
</div>
@@ -12,12 +12,16 @@
</div>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<div ng-show="success">
<h5 class="text-success"><strong>{{currentNode.name}}</strong> <localize key="editdatatype_wasMoved">was moved underneath</localize>&nbsp;<strong>{{target.name}}</strong></h5>
<div class="alert alert-success">
<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>
</div>
@@ -7,8 +7,10 @@
val-form-manager>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<umb-control-group label="@renamecontainer_enterNewFolderName" hide-label="false">
@@ -12,13 +12,16 @@
</div>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<div ng-show="success">
<h5 class="text-success">
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_copiedUnderneath">was copied underneath</localize>&nbsp;<strong>{{target.name}}</strong></h5>
<div class="alert alert-success">
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_copiedUnderneath">was copied underneath</localize>&nbsp;<strong>{{target.name}}</strong>
</div>
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
</div>
@@ -26,8 +26,8 @@ function DocumentTypesCreateController($scope, $location, navigationService, con
$scope.showCreateDocTypeCollection = function () {
$scope.model.creatingDoctypeCollection = true;
$scope.model.collectionCreateTemplate = true;
$scope.model.collectionItemCreateTemplate = true;
$scope.model.collectionCreateTemplate = !$scope.model.disableTemplates;
$scope.model.collectionItemCreateTemplate = !$scope.model.disableTemplates;
};
$scope.createContainer = function () {
@@ -16,8 +16,8 @@
<a href="" ng-click="createComponent()" class="umb-action-link">
<i class="large icon icon-item-arrangement"></i>
<span class="menu-label">
<localize ng-if="model.disableTemplates == false" key="create_documentTypeWithoutTemplate"></localize>
<localize ng-if="model.disableTemplates == true" key="content_documentType">Document type></localize>
<localize ng-if="model.disableTemplates === false" key="create_documentTypeWithoutTemplate"></localize>
<localize ng-if="model.disableTemplates === true" key="content_documentType">Document type></localize>
</span>
</a>
</li>
@@ -45,8 +45,10 @@
val-form-manager>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<umb-control-group label="Enter a folder name" hide-label="false">
@@ -69,20 +71,26 @@
val-form-manager>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<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 />
<umb-toggle on-click="model.collectionCreateTemplate = !model.collectionCreateTemplate" checked="model.collectionCreateTemplate"></umb-toggle>
<label>Create template for the Parent Document Type</label>
<span ng-if="model.disableTemplates === false">
<umb-toggle on-click="model.collectionCreateTemplate = !model.collectionCreateTemplate" checked="model.collectionCreateTemplate"></umb-toggle>
<label>Create template for the Parent Document Type</label>
</span>
</umb-control-group>
<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 />
<umb-toggle on-click="model.collectionItemCreateTemplate = !model.collectionItemCreateTemplate" checked="model.collectionItemCreateTemplate"></umb-toggle>
<label>Create template for the Item Document Type</label>
<span ng-if="model.disableTemplates === false">
<umb-toggle on-click="model.collectionItemCreateTemplate = !model.collectionItemCreateTemplate" checked="model.collectionItemCreateTemplate"></umb-toggle>
<label>Create template for the Item Document Type</label>
</span>
</umb-control-group>
<button type="submit" class="btn btn-primary"><localize key="general_create">Create</localize></button>
@@ -9,7 +9,7 @@
(function () {
"use strict";
function DocumentTypesEditController($scope, $routeParams, $injector, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService, overlayHelper, eventsService) {
function DocumentTypesEditController($scope, $routeParams, $injector, contentTypeResource, dataTypeResource, editorState, contentEditingHelper, formHelper, navigationService, iconHelper, contentTypeHelper, notificationsService, $filter, $q, localizationService, overlayHelper, eventsService, angularHelper) {
var vm = this;
var evts = [];
@@ -478,6 +478,15 @@
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);
@@ -12,13 +12,16 @@
</div>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<div ng-show="success">
<h5 class="text-success">
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_movedUnderneath">was moved underneath</localize>&nbsp;<strong>{{target.name}}</strong></h5>
<div class="alert alert-success">
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_movedUnderneath">was moved underneath</localize>&nbsp;<strong>{{target.name}}</strong>
</div>
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
</div>
@@ -6,8 +6,10 @@
val-form-manager>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<umb-control-group label="@renamecontainer_enterNewFolderName" hide-label="false">
@@ -12,13 +12,16 @@
</div>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<div ng-show="success">
<h5 class="text-success">
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_copiedUnderneath">was copied underneath</localize>&nbsp;<strong>{{target.name}}</strong></h5>
<div class="alert alert-success">
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_copiedUnderneath">was copied underneath</localize>&nbsp;<strong>{{target.name}}</strong>
</div>
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
</div>
@@ -12,12 +12,16 @@
</div>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<div ng-show="success">
<h5 class="text-success"><strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_movedUnderneath">was moved underneath</localize>&nbsp;<strong>{{target.name}}</strong></h5>
<div class="alert alert-success">
<strong>{{currentNode.name}}</strong> <localize key="contentTypeEditor_movedUnderneath">was moved underneath</localize>&nbsp;<strong>{{target.name}}</strong>
</div>
<button class="btn btn-primary" ng-click="nav.hideDialog()">Ok</button>
</div>
@@ -6,8 +6,10 @@
val-form-manager>
<div ng-show="error">
<h5 class="text-error">{{error.errorMsg}}</h5>
<p class="text-error">{{error.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{error.errorMsg}}</strong></div>
<div>{{error.data.message}}</div>
</div>
</div>
<umb-control-group label="@renamecontainer_enterNewFolderName" hide-label="false">
@@ -61,8 +61,10 @@
val-form-manager>
<div ng-show="vm.createFolderError">
<h5 class="text-error">{{vm.createFolderError.errorMsg}}</h5>
<p class="text-error">{{vm.createFolderError.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{vm.createFolderError.errorMsg}}</strong></div>
<div>{{vm.createFolderError.data.message}}</div>
</div>
</div>
<umb-control-group label="Enter a folder name" hide-label="false">
@@ -51,8 +51,10 @@
val-form-manager>
<div ng-show="vm.createFolderError">
<h5 class="text-error">{{vm.createFolderError.errorMsg}}</h5>
<p class="text-error">{{vm.createFolderError.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{vm.createFolderError.errorMsg}}</strong></div>
<div>{{vm.createFolderError.data.message}}</div>
</div>
</div>
<umb-control-group label="Enter a folder name" hide-label="false">
@@ -1,4 +1,4 @@
function booleanEditorController($scope, $rootScope, assetsService) {
function booleanEditorController($scope) {
function setupViewModel() {
$scope.renderModel = {
@@ -1,6 +1,10 @@
<div class="umb-property-editor umb-boolean" ng-controller="Umbraco.PropertyEditors.BooleanController">
<umb-toggle
checked="renderModel.value"
on-click="toggle()">
on-click="toggle()"
show-labels="{{model.config.labelOn ? 'true': 'false'}}"
label-position="right"
label-on="{{model.config.labelOn}}"
label-off="{{model.config.labelOn}}">
</umb-toggle>
</div>
@@ -1,5 +1,6 @@
<div ng-controller="Umbraco.PropertyEditors.ColorPickerController">
<div ng-if="!isConfigured">
<localize key="colorpicker_noColors">You haven't defined any colors</localize>
</div>
@@ -3,18 +3,22 @@
<div class="umb-prevalues-multivalues__left">
<input name="newColor" type="hidden" />
<label for="newColor" val-highlight="{{hasError}}">#{{newColor}}</label>
<input name="newLabel" type="text" ng-model="newLabel" class="umb-property-editor color-label" placeholder="Label" ng-show="labelEnabled" />
<input name="newLabel" type="text" ng-model="newLabel" focus-when="{{focusOnNew}}" class="umb-property-editor color-label" placeholder="Label" ng-show="labelEnabled" />
</div>
<div class="umb-prevalues-multivalues__right">
<button class="btn btn-info add" ng-click="add($event)"><localize key="general_add">Add</localize></button>
</div>
</div>
<div ui-sortable="sortableOptions" ng-model="model.value">
<div class="control-group umb-prevalues-multivalues__listitem color-picker-preval" ng-repeat="item in model.value">
<div class="control-group umb-prevalues-multivalues__listitem color-picker-preval" ng-repeat="item in model.value track by $id(item)">
<i class="icon icon-navigation handle"></i>
<div class="umb-prevalues-multivalues__left">
<div class="thumbnail span1" hex-bg-color="{{item.value}}" bg-orig="transparent"></div>
<div class="color-picker-prediv"><pre>#{{item.value}}</pre><span>{{item.label}}</span></div>
<div class="thumbnail span1" hex-bg-color="{{item.value}}" hex-bg-orig="transparent"></div>
<div class="color-picker-prediv">
<pre>#{{item.value}}</pre>
<span ng-bind="item.label" ng-if="!labelEnabled"></span>
<input type="text" ng-if="labelEnabled" ng-model="item.label" val-server="item_{{$index}}" required />
</div>
</div>
<div class="umb-prevalues-multivalues__right">
<a class="umb-node-preview__action umb-node-preview__action--red" ng-click="remove(item, $event)"><localize key="general_remove">Remove</localize></a>
@@ -5,8 +5,9 @@
var defaultLabel = null;
$scope.newColor = defaultColor;
$scope.newLavel = defaultLabel;
$scope.newLabel = defaultLabel;
$scope.hasError = false;
$scope.focusOnNew = false;
$scope.labels = {};
@@ -104,7 +105,6 @@
};
$scope.add = function (evt) {
evt.preventDefault();
if ($scope.newColor) {
@@ -117,7 +117,9 @@
value: $scope.newColor,
label: newLabel
});
$scope.newLabel = "";
$scope.hasError = false;
$scope.focusOnNew = true;
return;
}
@@ -35,6 +35,22 @@ function dateTimePickerController($scope, notificationsService, assetsService, a
}
};
//here we declare a special method which will be called whenever the value has changed from the server
//this is instead of doing a watch on the model.value = faster
$scope.model.onValueChanged = function (newVal, oldVal) {
if (newVal != oldVal) {
//check for c# System.DateTime.MinValue being passed as the clear indicator
var minDate = moment('0001-01-01');
var newDate = moment(newVal);
if (newDate.isAfter(minDate)) {
applyDate({ date: moment(newVal) });
} else {
$scope.clearDate();
}
}
};
//handles the date changing via the date picker
function applyDate(e) {
angularHelper.safeApply($scope, function() {
@@ -285,9 +285,11 @@ angular.module("umbraco")
event: event,
show: true,
submit: function(model) {
if (model.selectedItem) {
$scope.addControl(model.selectedItem, area, index);
$scope.editorOverlay.show = false;
$scope.editorOverlay = null;
}
}
};
});
@@ -0,0 +1,25 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.IdWithGuidValueController
* @function
*
* @description
* The controller for the idwithguid property editor, which formats the ID as normal
* with the GUID in smaller text below, as used across the backoffice.
*/
function IdWithGuidValueController($rootScope, $scope, $filter) {
function formatDisplayValue() {
if ($scope.model.value.length > 1) {
$scope.displayid = $scope.model.value[0];
$scope.displayguid = $scope.model.value[1];
} else {
$scope.displayid = $scope.model.value;
}
}
//format the display value on init:
formatDisplayValue();
}
angular.module('umbraco').controller("Umbraco.PropertyEditors.IdWithGuidValueController", IdWithGuidValueController);
@@ -0,0 +1,4 @@
<div class="umb-editor umb-readonlyvalue umb-idwithguidvalue" ng-controller="Umbraco.PropertyEditors.IdWithGuidValueController">
<div>{{ displayid }}</div>
<small>{{ displayguid }}</small>
</div>
@@ -171,7 +171,7 @@ angular.module('umbraco')
})
.run(function (mediaHelper, umbRequestHelper) {
if (mediaHelper && mediaHelper.registerFileResolver) {
//NOTE: The 'entity' can be either a normal media entity or an "entity" returned from the entityResource
// they contain different data structures so if we need to query against it we need to be aware of this.
mediaHelper.registerFileResolver("Umbraco.ImageCropper", function (property, entity, thumbnail) {
@@ -2,7 +2,6 @@
ng-controller="Umbraco.PropertyEditors.ImageCropperController">
<ng-form name="imageCropperForm">
<umb-property-file-upload culture="{{model.culture}}"
property-alias="{{model.alias}}"
value="model.value.src"
@@ -17,7 +16,6 @@
<div class="umb-cropper__container">
<i ng-click="done()" class="icon icon-delete btn-round umb-close-cropper"></i>
<div>
<umb-image-crop height="{{currentCrop.height}}"
width="{{currentCrop.width}}"
@@ -42,7 +40,7 @@
</umb-image-gravity>
<a href class="btn btn-link btn-crop-delete" ng-click="clear()"><i class="icon-delete red"></i> <localize key="content_uploadClear">Remove file</localize></a>
</div>
<ul ng-if="!imageSrc.endsWith('.svg')" class="umb-sortable-thumbnails cropList clearfix">
<li ng-repeat=" (key,value) in model.value.crops" ng-class="{'current':currentCrop.alias === value.alias}" ng-click="crop(value)">
@@ -2,6 +2,9 @@
var backspaceHits = 0;
// Set the visible prompt to -1 to ensure it will not be visible
$scope.promptIsVisible = "-1";
$scope.sortableOptions = {
axis: 'y',
containment: 'parent',
@@ -89,6 +92,9 @@
};
$scope.remove = function (index) {
// Make sure not to trigger other prompts when remove is triggered
$scope.hidePrompt();
var remainder = [];
for (var x = 0; x < $scope.model.value.length; x++) {
if (x !== index) {
@@ -98,6 +104,20 @@
$scope.model.value = remainder;
};
$scope.showPrompt = function (idx, item){
var i = $scope.model.value.indexOf(item);
// Make the prompt visible for the clicked tag only
if (i === idx) {
$scope.promptIsVisible = i;
}
}
$scope.hidePrompt = function(){
$scope.promptIsVisible = "-1";
}
}
angular.module("umbraco").controller("Umbraco.PropertyEditors.MultipleTextBoxController", MultipleTextBoxController);
angular.module("umbraco").controller("Umbraco.PropertyEditors.MultipleTextBoxController", MultipleTextBoxController);
@@ -5,11 +5,20 @@
<input type="text" name="item_{{$index}}" ng-model="item.value" class="umb-property-editor umb-textstring textstring"
ng-keyup="addRemoveOnKeyDown($event, $index)" focus-when="{{item.hasFocus}}"/>
<i class="icon icon-navigation handle" localize="title" title="@general_move"></i>
<a prevent-default href="" class="remove" localize="title" title="@content_removeTextBox"
ng-show="model.value.length > model.config.min"
ng-click="remove($index)">
<i class="icon icon-remove"></i>
</a>
<div class="umb-multiple-textbox__confirm" ng-show="model.value.length > model.config.min">
<button class="umb-multiple-textbox__confirm-action" type="button" prevet-default ng-click="showPrompt($index, item)" localize="title" title="@content_removeTextBox">
<i class="icon-trash"></i>
</button>
<umb-confirm-action
ng-if="promptIsVisible === $index"
direction="left"
on-confirm="remove($index)"
on-cancel="hidePrompt()">
</umb-confirm-action>
</div>
</div>
</div>
<a prevent-default href="" class="add-link" localize="title" title="@content_addTextBox"
@@ -37,6 +37,18 @@
});
});
$scope.selectableDocTypesFor = function (config) {
// return all doctypes that are:
// 1. either already selected for this config, or
// 2. not selected in any other config
return _.filter($scope.model.docTypes, function (docType) {
return docType.alias === config.ncAlias || !_.find($scope.model.value, function(c) {
return docType.alias === c.ncAlias;
});
});
}
if (!$scope.model.value) {
$scope.model.value = [];
$scope.add();
@@ -231,6 +243,7 @@ angular.module("umbraco").controller("Umbraco.PropertyEditors.NestedContent.Prop
cursor: "move",
handle: ".umb-nested-content__icon--move",
start: function (ev, ui) {
updateModel();
// Yea, yea, we shouldn't modify the dom, sue me
$("#umb-nested-content--" + $scope.model.id + " .umb-rte textarea").each(function () {
tinymce.execCommand('mceRemoveEditor', false, $(this).attr('id'));
@@ -22,7 +22,7 @@
</td>
<td>
<select id="{{model.alias}}_doctype_select"
ng-options="dt.alias as dt.name for dt in model.docTypes | orderBy: 'name'"
ng-options="dt.alias as dt.name for dt in selectableDocTypesFor(config) | orderBy: 'name'"
ng-model="config.ncAlias" required></select>
</td>
<td>
@@ -1,5 +1,5 @@
<div ng-controller="Umbraco.PropertyEditors.RTEController" class="umb-property-editor umb-rte">
<div ng-if="isLoading"><localize key="general_loading">Loading</localize>...</div>
<div class="-loading" ng-if="isLoading"><localize key="general_loading">Loading</localize>...</div>
<textarea
ng-style="{ visibility : isLoading ? 'hidden' : 'visible'}"
ng-model="model.value"
@@ -26,8 +26,10 @@
val-form-manager>
<div ng-show="vm.createFolderError">
<h5 class="text-error">{{vm.createFolderError.errorMsg}}</h5>
<p class="text-error">{{vm.createFolderError.data.message}}</p>
<div class="alert alert-error">
<div><strong>{{vm.createFolderError.errorMsg}}</strong></div>
<div>{{vm.createFolderError.data.message}}</div>
</div>
</div>
<umb-control-group label="Enter a folder name" hide-label="false">
@@ -145,15 +145,15 @@
}, function (err) {
if (suppressNotification) {
vm.page.saveButtonState = "error";
vm.page.saveButtonState = "error";
localizationService.localizeMany(["speechBubbles_validationFailedHeader", "speechBubbles_validationFailedMessage"]).then(function(data){
var header = data[0];
var message = data[1];
notificationsService.error(header, message);
});
localizationService.localizeMany(["speechBubbles_validationFailedHeader", "speechBubbles_validationFailedMessage"]).then(function (data) {
var header = data[0];
var message = data[1];
notificationsService.error(header, message);
});
}
});
};
@@ -93,8 +93,14 @@
//in the ASP.NET Identity world, this config option will allow an admin user to change another user's password
//if the user has access to the user section. So if this editor is being access, the user of course has access to this section.
//the authorization check is also done on the server side when submitted.
vm.changePasswordModel.config.allowManuallyChangingPassword = !vm.user.isCurrentUser;
// only update the setting if not the current logged in user, otherwise leave the value as it is
// currently set in the web.config
if (!vm.user.isCurrentUser)
{
vm.changePasswordModel.config.allowManuallyChangingPassword = true;
}
vm.loading = false;
});
});
@@ -15,7 +15,7 @@
<umb-editor-container>
<div ng-if="!vm.loading" class="umb-packages-view-wrapper" style="padding: 0;">
<div ng-if="!vm.loading" class="umb-user-details-view-wrapper" style="padding: 0;">
<umb-editor-sub-views
ng-if="!vm.loading"
@@ -65,4 +65,4 @@
</form>
</div>
</div>
@@ -1,6 +1,6 @@
<div class="umb-package-details">
<div class="umb-user-details-details">
<div class="umb-package-details__main-content">
<div class="umb-user-details-details__main-content">
<umb-box>
<ng-form name="userProfileForm">
@@ -103,8 +103,8 @@
<a href=""
ng-if="!model.user.isCurrentUser"
style="max-width: 100%;"
class="umb-node-preview-add"
id="content-start-add"
ng-click="model.openContentPicker()"
prevent-default>
<localize key="general_add">Add</localize>
@@ -129,9 +129,9 @@
<a href=""
ng-if="!model.user.isCurrentUser"
style="max-width: 100%;"
class="umb-node-preview-add"
class="umb-node-preview-add"
ng-click="model.openMediaPicker()"
id="media-start-add"
prevent-default>
<localize key="general_add">Add</localize>
</a>
@@ -173,14 +173,14 @@
</umb-box>
</div>
<div class="umb-package-details__sidebar">
<div class="umb-user-details-details__sidebar">
<umb-box>
<umb-box-content>
<!-- Avatar -->
<div style="margin-bottom: 20px; padding-bottom: 20px; border-bottom: 1px solid #d8d7d9;">
<div class="umb-user-details-avtar">
<ng-form name="avatarForm" class="flex flex-column justify-center items-center">
<umb-avatar style="margin-bottom: 15px;"
@@ -224,9 +224,9 @@
</div>
<!-- Actions -->
<div style="margin-bottom: 20px;">
<div class="umb-user-details-actions">
<div style="margin-bottom: 10px;">
<div>
<umb-button ng-if="model.user.userDisplayState.key === 'Disabled' && !model.user.isCurrentUser"
type="button"
button-style="[success,block]"
@@ -238,7 +238,7 @@
</umb-button>
</div>
<div style="margin-bottom: 10px;">
<div>
<umb-button ng-if="model.user.userDisplayState.key === 'LockedOut' && !model.user.isCurrentUser"
type="button"
button-style="[success,block]"
@@ -249,8 +249,7 @@
size="s">
</umb-button>
</div>
<div style="margin-bottom: 10px;">
<div>
<umb-button ng-if="model.user.userDisplayState.key !== 'Disabled' && model.user.userDisplayState.key !== 'Invited' && !model.user.isCurrentUser"
type="button"
button-style="[info,block]"
@@ -261,8 +260,8 @@
size="s">
</umb-button>
</div>
<umb-button type="button" ng-if="model.changePasswordModel.isChanging === false && model.user.userDisplayState.key !== 'Invited'"
<div>
<umb-button type="button" ng-if="model.changePasswordModel.isChanging === false && model.user.userDisplayState.key !== 'Invited'"
button-style="[info,block]"
action="model.toggleChangePassword()"
label="Change password"
@@ -270,15 +269,17 @@
state="changePasswordButtonState"
size="s">
</umb-button>
<umb-button type="button" ng-if="!model.user.lastLoginDate"
button-style="[danger,block]"
action="model.deleteNonLoggedInUser()"
label="Delete"
label-key="user_deleteUser"
state="deleteNotLoggedInUserButtonState"
size="s">
</umb-button>
</div>
<div>
<umb-button type="button" ng-if="!model.user.lastLoginDate"
button-style="[danger,block]"
action="model.deleteNonLoggedInUser()"
label="Delete"
label-key="user_deleteUser"
state="deleteNotLoggedInUserButtonState"
size="s">
</umb-button>
</div>
<ng-form ng-if="model.changePasswordModel.isChanging" name="passwordForm" class="block-form" val-form-manager>
@@ -302,26 +303,26 @@
</div>
<!-- User stats -->
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<div class="umb-user-details-details__information-item">
<div class="umb-user-details-details__information-item-label">
<localize key="general_status">Status</localize>:
</div>
<div class="umb-package-details__information-item-content">
<div class="umb-user-details-details__information-item-content">
<umb-badge style="margin-top: 4px;" size="s" color="{{model.user.userDisplayState.color}}">
{{model.user.userDisplayState.name}}
</umb-badge>
</div>
</div>
<div style="margin-bottom: 10px;" ng-if="model.user.userDisplayState.key === 'Invited' && !model.user.isCurrentUser">
<div ng-if="model.user.userDisplayState.key === 'Invited' && !model.user.isCurrentUser">
<textarea name="resendInviteMessage"
type="text"
class="input-block-level"
localize="placeholder"
placeholder="@placeholders_enterMessage"
ng-model="model.resendInviteMessage"
rows="4">
</textarea>
type="text"
class="input-block-level"
localize="placeholder"
placeholder="@placeholders_enterMessage"
ng-model="model.resendInviteMessage"
rows="4">
</textarea>
<umb-button type="button"
button-style="[info,block]"
action="model.resendInvite()"
@@ -332,30 +333,30 @@
</umb-button>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<div class="umb-user-details-details__information-item">
<div class="umb-user-details-details__information-item-label">
<localize key="user_lastLogin">Last login</localize>:
</div>
<div class="umb-package-details__information-item-content">
<div class="umb-user-details-details__information-item-content">
<span ng-if="model.user.lastLoginDate">{{ model.user.formattedLastLogin }}</span>
<span ng-if="!model.user.lastLoginDate">{{ model.user.name | umbWordLimit:1 }} <localize key="user_noLogin">has not logged in yet</localize></span>
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<div class="umb-user-details-details__information-item">
<div class="umb-user-details-details__information-item-label">
<localize key="user_failedPasswordAttempts">Failed login attempts</localize>:
</div>
<div class="umb-package-details__information-item-content">
<div class="umb-user-details-details__information-item-content">
{{ model.user.failedPasswordAttempts }}
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<div class="umb-user-details-details__information-item">
<div class="umb-user-details-details__information-item-label">
<localize key="user_lastLockoutDate">Last lockout date</localize>:
</div>
<div class="umb-package-details__information-item-content">
<div class="umb-user-details-details__information-item-content">
<span ng-if="model.user.lastLockoutDate === '0001-01-01T00:00:00'">
{{ model.user.name | umbWordLimit:1 }} <localize key="user_noLockouts">hasn't been locked out</localize>
</span>
@@ -363,11 +364,11 @@
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<div class="umb-user-details-details__information-item">
<div class="umb-user-details-details__information-item-label">
<localize key="user_lastPasswordChangeDate">Password is last changed</localize>:
</div>
<div class="umb-package-details__information-item-content">
<div class="umb-user-details-details__information-item-content">
<span ng-if="model.user.lastPasswordChangeDate === '0001-01-01T00:00:00'">
<localize key="user_noPasswordChange">The password hasn't been changed</localize>
</span>
@@ -375,28 +376,43 @@
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<div class="umb-user-details-details__information-item">
<div class="umb-user-details-details__information-item-label">
<localize key="user_createDate">User is created</localize>:
</div>
<div class="umb-package-details__information-item-content">
<div class="umb-user-details-details__information-item-content">
{{ model.user.formattedCreateDate }}
</div>
</div>
<div class="umb-package-details__information-item">
<div class="umb-package-details__information-item-label">
<div class="umb-user-details-details__information-item">
<div class="umb-user-details-details__information-item-label">
<localize key="user_updateDate">User is last updated</localize>:
</div>
<div class="umb-package-details__information-item-content">
<div class="umb-user-details-details__information-item-content">
{{ model.user.formattedUpdateDate }}
</div>
</div>
</umb-box-content>
<div class="umb-user-details-details__information-item">
<div class="umb-user-details-details__information-item-label">
<localize key="general_id">Id</localize>:
</div>
<div class="umb-user-details-details__information-item-content">
{{ model.user.id }}
</div>
<div class="umb-user-details-details__information-item-content">
<small>{{ model.user.key }}</small>
</div>
</div>
</div>
</umb-box-content>
</umb-box>
</div>
</div>
@@ -32,7 +32,7 @@
@Html.Partial(Model.PreviewExtendedHeaderView)
}
<div id="demo-iframe-wrapper" ng-show="frameLoaded && pageUrl" class="{{previewDevice.css}}">
<div id="demo-iframe-wrapper" class="{{previewDevice.css}}">
<preview-i-frame src="pageUrl" on-loaded="onFrameLoaded(iframe)"></preview-i-frame>
</div>
<div class="canvasdesigner" ng-init="showDevicesPreview = true; showDevices = !@(disableDevicePreview);" ng-mouseenter="positionSelectedHide()">
@@ -145,6 +145,7 @@
<key alias="rollback">Brugeren har tilbagerullet indholdet til en tidligere tilstand</key>
<key alias="sendtopublish">Brugeren har sendt indholdet til udgivelse</key>
<key alias="sendtotranslate">Brugeren har sendt indholdet til oversættelse</key>
<key alias="sort">Brugeren har sorteret de underliggende sider</key>
<key alias="smallCopy">Kopieret</key>
<key alias="smallPublish">Udgivet</key>
<key alias="smallMove">Flyttet</key>
@@ -154,6 +155,7 @@
<key alias="smallRollBack">Indhold tilbagerullet</key>
<key alias="smallSendToPublish">Sendt til udgivelse</key>
<key alias="smallSendToTranslate">Sendt til oversættelse</key>
<key alias="smallSort">Sorteret</key>
</area>
<area alias="changeDocType">
<key alias="changeDocTypeInstruction">For at skifte det valgte indholds dokumenttype, skal du først vælge en ny dokumenttype, som er gyldig på denne placering.</key>
@@ -254,7 +256,7 @@
<area alias="media">
<key alias="clickToUpload">Klik for at uploade</key>
<key alias="orClickHereToUpload">eller klik her for at vælge filer</key>
<key alias="dragFilesHereToUpload">Du kan trække filer herind for at uploade</key>
<key alias="dragFilesHereToUpload">Du kan trække filer herind for at uploade.</key>
<key alias="disallowedFileType">Kan ikke uploade denne fil, den har ikke en godkendt filtype</key>
<key alias="maxFileSize">Maks filstørrelse er</key>
<key alias="mediaRoot">Medie rod</key>
@@ -421,6 +423,8 @@
<key alias="email">Indtast din e-mail</key>
<key alias="enterMessage">Indtast en besked...</key>
<key alias="usernameHint">Dit brugernavn er typisk din e-mailadresse</key>
<key alias="enterAlias">Indtast alias...</key>
<key alias="generatingAlias">Genererer alias...</key>
</area>
<area alias="editcontenttype">
<key alias="allowedchildnodetypes">Tilladte typer</key>
@@ -530,6 +534,7 @@
<key alias="hide">Skjul</key>
<key alias="history">Historik</key>
<key alias="icon">Ikon</key>
<key alias="id">Id</key>
<key alias="import">Importer</key>
<key alias="innerMargin">Indre margen</key>
<key alias="insert">Indsæt</key>
@@ -918,6 +923,8 @@ Mange hilsner fra Umbraco robotten
</area>
<area alias="imagecropper">
<key alias="reset">Nulstil</key>
<key alias="updateEditCrop">Acceptér</key>
<key alias="undoEditCrop">Fortryd</key>
</area>
<area alias="rollback">
<key alias="currentVersion">Nuværende version</key>
@@ -74,15 +74,19 @@
<asp:Content ContentPlaceHolderID="body" runat="server">
<style> .umb-dialog { overflow: auto; } .umb-dialog-footer { position: relative; }</style>
<input id="tempFile" type="hidden" name="tempFile" runat="server" />
<cc1:Feedback ID="feedback" runat="server" />
<asp:Panel ID="p_feedback" runat="server" Visible="False">
<div class="alert alert-success">
<asp:Literal runat="server" ID="feedback_text"></asp:Literal>
</div>
<button class="btn btn-primary" onclick="UmbClientMgr.closeModalWindow()"><%= umbraco.ui.Text("general", "ok")%></button>
</asp:Panel>
<asp:Panel ID="p_mode" runat="server" CssClass="pa-umb-overlay">
<div class="umg-dialog-body">
<div class="umb-dialog-body">
<cc1:Pane ID="pane_chooseMode" runat="server" Text="Choose how to restict access to this page">
@@ -116,74 +120,72 @@
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<a href="#" class="btn btn-link" onclick="UmbClientMgr.closeModalWindow()"><%=Services.TextService.Localize("cancel")%></a>
<asp:Button ID="bt_selectMode" runat="server" Text="select" CssClass="btn btn-primary" OnClick="selectMode" />
<asp:Button ID="bt_selectMode" runat="server" CssClass="btn btn-primary" OnClick="selectMode" />
</div>
</asp:Panel>
<asp:Panel ID="p_setup" runat="server" Visible="false" CssClass="pa-umb-overlay">
<div class="umb-dialog-body">
<cc1:Pane ID="pane_simple" runat="server" Visible="false" Text="Set the login and password for this page" CssClass="pa-umb-overlay">
<cc1:Pane ID="pane_simple" runat="server" Visible="false" Text="Set the login and password for this page" CssClass="pa-umb-overlay">
<div class="pa-form">
<cc1:PropertyPanel Text="Login" ID="pp_login" runat="server">
<asp:TextBox ID="simpleLogin" runat="server" Width="250px"></asp:TextBox>
<asp:Label runat="server" ID="SimpleLoginLabel" Visible="False"></asp:Label>
</cc1:PropertyPanel>
</div>
<div class="pa-form">
<cc1:PropertyPanel Text="Password" ID="pp_pass" runat="server">
<asp:TextBox ID="simplePassword" runat="server" Width="250px"></asp:TextBox>
</cc1:PropertyPanel>
</div>
<asp:CustomValidator CssClass="pa-validation-message error" runat="server" ID="SimpleLoginNameValidator" Display="Dynamic" EnableViewState="False">
<p class="alert">Member name already exists, click <asp:LinkButton runat="server" OnClick="ChangeOnClick" CssClass="btn btn-mini btn-warning">Change</asp:LinkButton> to use a different name or Update to continue</p>
</asp:CustomValidator>
</cc1:Pane>
<cc1:Pane ID="pane_advanced" runat="server" Visible="false" Text="Role based protection">
<cc1:PropertyPanel ID="PropertyPanel3" runat="server">
<p><%= Services.TextService.Localize("publicAccess/paSelectRoles")%></p>
</cc1:PropertyPanel>
<cc1:PropertyPanel ID="PropertyPanel2" runat="server">
<asp:PlaceHolder ID="groupsSelector" runat="server"></asp:PlaceHolder>
</cc1:PropertyPanel>
</cc1:Pane>
<asp:Panel ID="p_buttons" runat="server" Visible="false" CssClass="pa-umb-overlay">
<cc1:Pane runat="server" ID="pane_pages" Text="Select the pages that contain login form and error messages">
<cc1:PropertyPanel runat="server" ID="pp_loginPage" CssClass="pa-select-pages">
<small class="umb-detail">
<%=Services.TextService.Localize("paLoginPageHelp")%>
</small>
<div class="pa-choose-page">
<asp:PlaceHolder ID="ph_loginpage" runat="server" />
<div class="pa-form">
<cc1:PropertyPanel Text="Login" ID="pp_login" runat="server">
<asp:TextBox ID="simpleLogin" runat="server" Width="250px"></asp:TextBox>
<asp:Label runat="server" ID="SimpleLoginLabel" Visible="False"></asp:Label>
</cc1:PropertyPanel>
</div>
<asp:CustomValidator ErrorMessage="Please pick a login page" runat="server" ID="cv_loginPage" ForeColor="Red" />
</cc1:PropertyPanel>
<cc1:PropertyPanel runat="server" ID="pp_errorPage" CssClass="pa-select-pages">
<small class="umb-detail">
<%=Services.TextService.Localize("paErrorPageHelp")%>
</small>
<div class="pa-choose-page">
<asp:PlaceHolder ID="ph_errorpage" runat="server" />
<div class="pa-form">
<cc1:PropertyPanel Text="Password" ID="pp_pass" runat="server">
<asp:TextBox ID="simplePassword" runat="server" Width="250px"></asp:TextBox>
</cc1:PropertyPanel>
</div>
<asp:CustomValidator ErrorMessage="Please pick an error page" runat="server" ID="cv_errorPage" ForeColor="Red" />
</cc1:PropertyPanel>
</cc1:Pane>
<asp:CustomValidator CssClass="pa-validation-message error" runat="server" ID="SimpleLoginNameValidator" Display="Dynamic" EnableViewState="False">
<p class="alert">Member name already exists, click <asp:LinkButton runat="server" OnClick="ChangeOnClick" CssClass="btn btn-mini btn-warning">Change</asp:LinkButton> to use a different name or Update to continue</p>
</asp:CustomValidator>
</cc1:Pane>
<cc1:Pane ID="pane_advanced" runat="server" Visible="false" Text="Role based protection">
<cc1:PropertyPanel ID="PropertyPanel3" runat="server">
<p><%= Services.TextService.Localize("publicAccess/paSelectRoles")%></p>
</cc1:PropertyPanel>
<cc1:PropertyPanel ID="PropertyPanel2" runat="server">
<asp:PlaceHolder ID="groupsSelector" runat="server"></asp:PlaceHolder>
</cc1:PropertyPanel>
</cc1:Pane>
<cc1:Pane runat="server" ID="pane_pages" Text="Select the pages that contain login form and error messages">
<cc1:PropertyPanel runat="server" ID="pp_loginPage" CssClass="pa-select-pages">
<small class="umb-detail">
<%=Services.TextService.Localize("paLoginPageHelp")%>
</small>
<div class="pa-choose-page">
<asp:PlaceHolder ID="ph_loginpage" runat="server" />
</div>
<asp:CustomValidator ErrorMessage="Please pick a login page" runat="server" ID="cv_loginPage" ForeColor="Red" />
</cc1:PropertyPanel>
<cc1:PropertyPanel runat="server" ID="pp_errorPage" CssClass="pa-select-pages">
<small class="umb-detail">
<%=Services.TextService.Localize("paErrorPageHelp")%>
</small>
<div class="pa-choose-page">
<asp:PlaceHolder ID="ph_errorpage" runat="server" />
</div>
<asp:CustomValidator ErrorMessage="Please pick an error page" runat="server" ID="cv_errorPage" ForeColor="Red" />
</cc1:PropertyPanel>
</cc1:Pane>
</div>
<div class="umb-dialog-footer btn-toolbar umb-btn-toolbar">
<a href="#" class="btn btn-link" onclick="UmbClientMgr.closeModalWindow()"><%=Services.TextService.Localize("cancel")%></a>
<asp:Button ID="bt_protect" CssClass="btn btn-primary" runat="server" OnCommand="protect_Click"></asp:Button>
<asp:Button ID="bt_buttonRemoveProtection" CssClass="btn btn-danger" runat="server" Visible="False" OnClick="buttonRemoveProtection_Click" />
</div>
</asp:Panel>
<input id="errorId" type="hidden" runat="server" /><input id="loginId" type="hidden" runat="server" />
@@ -13,43 +13,6 @@
var submitOnEnter = true;
</script>
<style type="text/css">
.propertyItemheader {
width: 140px !Important;
}
.diff {
margin-top: 10px;
height: 100%;
overflow: auto;
border-top: 1px solid #efefef;
padding: 5px;
}
.diff table td {
border-bottom: 1px solid #ccc;
padding: 3px;
}
.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;
}
.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;
}
.diff .diffnotice {
text-align: center;
margin-bottom: 10px;
}
</style>
</asp:Content>
<asp:Content ContentPlaceHolderID="body" runat="server">
@@ -63,7 +26,7 @@
<asp:Literal ID="currentVersionTitle" runat="server" />
<small>(<asp:Literal ID="currentVersionMeta" runat="server" />)</small></cc1:PropertyPanel>
<cc1:PropertyPanel ID="pp_rollBackTo" Text="Rollback to" runat="server">
<asp:DropDownList OnSelectedIndexChanged="version_load" ID="allVersions" runat="server" Width="400px" AutoPostBack="True" CssClass="guiInputTextTiny" />
<asp:DropDownList OnSelectedIndexChanged="version_load" ID="allVersions" runat="server" AutoPostBack="True" CssClass="guiInputTextTiny diffDropdown" />
</cc1:PropertyPanel>
<cc1:PropertyPanel id="pp_view" Text="View" runat="server">
@@ -76,7 +39,7 @@
</cc1:PropertyPanel>
</cc1:Pane>
<asp:Panel ID="diffPanel" Visible="false" runat="server" Height="300px">
<asp:Panel ID="diffPanel" Visible="false" runat="server" CssClass="diffPanel">
<div class="diff">
<div class="diffnotice">
<p>
@@ -84,7 +47,7 @@
</p>
</div>
<table border="0" style="width: 95%;">
<table>
<asp:Literal ID="propertiesCompare" runat="server"></asp:Literal>
</table>
</div>
@@ -124,7 +124,7 @@
<provider name="Twitgoo" type="Umbraco.Web.Media.EmbedProviders.Twitgoo, Umbraco.Web">
<urlShemeRegex><![CDATA[twitgoo\.com/]]></urlShemeRegex>
</provider>
<!-- Twitter Settigs -->
<!-- Twitter Settings -->
<provider name="Twitter" type="Umbraco.Web.Media.EmbedProviders.OEmbedJson, Umbraco.Web">
<urlShemeRegex><![CDATA[twitter\.com/]]></urlShemeRegex>
<apiEndpoint><![CDATA[https://publish.twitter.com/oembed]]></apiEndpoint>
@@ -124,7 +124,7 @@
<provider name="Twitgoo" type="Umbraco.Web.Media.EmbedProviders.Twitgoo, Umbraco.Web">
<urlShemeRegex><![CDATA[twitgoo\.com/]]></urlShemeRegex>
</provider>
<!-- Twitter Settigs -->
<!-- Twitter Settings -->
<provider name="Twitter" type="Umbraco.Web.Media.EmbedProviders.OEmbedJson, Umbraco.Web">
<urlShemeRegex><![CDATA[twitter\.com/]]></urlShemeRegex>
<apiEndpoint><![CDATA[https://publish.twitter.com/oembed]]></apiEndpoint>
+5
View File
@@ -82,9 +82,14 @@
<system.net>
<mailSettings>
<!--
If you need Umbraco to send out system mails (like reset password and invite user),
you must configure your SMTP host here - for example:
<smtp from="noreply@example.com">
<network host="127.0.0.1" userName="username" password="password" />
</smtp>
-->
</mailSettings>
</system.net>
@@ -57,7 +57,7 @@ namespace Umbraco.Web.Editors
var keepOnlyKeys = new Dictionary<string, string[]>
{
{"umbracoUrls", new[] {"authenticationApiBaseUrl", "serverVarsJs", "externalLoginsUrl", "currentUserApiBaseUrl"}},
{"umbracoSettings", new[] {"allowPasswordReset", "imageFileTypes", "maxFileSize", "loginBackgroundImage"}},
{"umbracoSettings", new[] {"allowPasswordReset", "imageFileTypes", "maxFileSize", "loginBackgroundImage", "canSendRequiredEmail"}},
{"application", new[] {"applicationPath", "cacheBuster"}},
{"isDebuggingEnabled", new string[] { }},
{"features", new [] {"disabledFeatures"}}
@@ -326,6 +326,7 @@ namespace Umbraco.Web.Editors
{"allowPasswordReset", UmbracoConfig.For.UmbracoSettings().Security.AllowPasswordReset},
{"loginBackgroundImage", UmbracoConfig.For.UmbracoSettings().Content.LoginBackgroundImage},
{"showUserInvite", EmailSender.CanSendRequiredEmail},
{"canSendRequiredEmail", EmailSender.CanSendRequiredEmail},
}
},
{
+2 -2
View File
@@ -555,7 +555,7 @@ namespace Umbraco.Web.Editors
/// <returns></returns>
[FileUploadCleanupFilter]
[ContentSaveValidation]
public ContentItemDisplay PostSaveBlueprint([ModelBinder(typeof(ContentItemBinder))] ContentItemSave contentItem)
public ContentItemDisplay PostSaveBlueprint([ModelBinder(typeof(BlueprintItemBinder))] ContentItemSave contentItem)
{
var contentItemDisplay = PostSaveInternal(contentItem,
content =>
@@ -1068,7 +1068,7 @@ namespace Umbraco.Web.Editors
var contentService = Services.ContentService;
// Save content with new sort order and update content xml in db accordingly
if (contentService.Sort(sorted.IdSortOrder) == false)
if (contentService.Sort(sorted.IdSortOrder, Security.CurrentUser.Id) == false)
{
Logger.Warn<ContentController>("Content sorting failed, this was probably caused by an event being cancelled");
return Request.CreateValidationErrorResponse("Content sorting failed, this was probably caused by an event being cancelled");
@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using Umbraco.Core.Logging;
using Umbraco.Core.Migrations.Install;
using Umbraco.Core.Services;
namespace Umbraco.Web.HealthCheck.Checks.DataIntegrity
{
/// <summary>
/// U4-9544 Health check to detect if the database has any missing indexes or constraints
/// </summary>
[HealthCheck(
"0873D589-2064-4EA3-A152-C43417FE00A4",
"Database Schema Validation",
Description = "This checks the Umbraco database by doing a comparison of current indexes and schema items with the current state of the database and returns any problems it found. Useful to detect if the database hasn't been upgraded correctly.",
Group = "Data Integrity")]
public class DatabaseSchemaValidationHealthCheck : HealthCheck
{
private readonly DatabaseBuilder _databaseBuilder;
private readonly ILocalizedTextService _textService;
private readonly ILogger _logger;
public DatabaseSchemaValidationHealthCheck(DatabaseBuilder databaseBuilder, ILocalizedTextService textService, ILogger logger)
{
_databaseBuilder = databaseBuilder ?? throw new ArgumentNullException(nameof(databaseBuilder));
_textService = textService ?? throw new ArgumentNullException(nameof(textService));
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public override HealthCheckStatus ExecuteAction(HealthCheckAction action)
{
return CheckDatabase();
}
public override IEnumerable<HealthCheckStatus> GetStatus()
{
//return the statuses
return new[] { CheckDatabase() };
}
private HealthCheckStatus CheckDatabase()
{
var results = _databaseBuilder.ValidateDatabaseSchema();
_logger.Warn<DatabaseSchemaValidationHealthCheck>(_textService.Localize("databaseSchemaValidationCheckDatabaseLogMessage"));
foreach(var error in results.Errors)
{
_logger.Warn<DatabaseSchemaValidationHealthCheck>("{Error} : {ErrorDetail}", error.Item1, error.Item2);
}
if(results.Errors.Count > 0)
{
return new HealthCheckStatus(_textService.Localize("healthcheck/databaseSchemaValidationCheckDatabaseErrors", new[] { results.Errors.Count.ToString() }))
{
ResultType = StatusResultType.Error,
View = "Umbraco.Dashboard.DatabaseSchemaValidationController"
};
}
return new HealthCheckStatus(_textService.Localize("healthcheck/databaseSchemaValidationCheckDatabaseOk"))
{
ResultType = StatusResultType.Success
};
}
}
}
+23
View File
@@ -2,11 +2,34 @@ namespace Umbraco.Web.Models
{
public enum ImageCropMode
{
/// <summary>
/// Resizes the image to the given dimensions. If the set dimensions do not match the aspect ratio of the original image then the output is cropped to match the new aspect ratio.
/// </summary>
Crop,
/// <summary>
/// Resizes the image to the given dimensions. If the set dimensions do not match the aspect ratio of the original image then the output is resized to the maximum possible value in each direction while aintaining the original aspect ratio.
/// </summary>
Max,
/// <summary>
/// Resizes the image to the given dimensions. If the set dimensions do not match the aspect ratio of the original image then the output is stretched to match the new aspect ratio.
/// </summary>
Stretch,
/// <summary>
/// Passing a single dimension will automatically preserve the aspect ratio of the original image. If the requested aspect ratio is different then the image will be padded to fit.
/// </summary>
Pad,
/// <summary>
/// When upscaling an image the image pixels themselves are not resized, rather the image is padded to fit the given dimensions.
/// </summary>
BoxPad,
/// <summary>
/// Resizes the image until the shortest side reaches the set given dimension. This will maintain the aspect ratio of the original image. Upscaling is disabled in this mode and the original image will be returned if attempted.
/// </summary>
Min
}
}
@@ -15,7 +15,7 @@
}
/// <summary>
/// Sets the node style to show that it is currently protected publicly
/// Sets the node style to show that it is a container type
/// </summary>
/// <param name="treeNode"></param>
public static void SetContainerStyle(this TreeNode treeNode)
@@ -5,7 +5,7 @@ using Umbraco.Core.PropertyEditors.Validators;
namespace Umbraco.Web.PropertyEditors
{
[DataEditor(Constants.PropertyEditors.Aliases.EmailAddress, "Email address", "email", Icon="icon-message")]
[DataEditor(Constants.PropertyEditors.Aliases.EmailAddress, "Email address", "email", IsParameterEditor = true, Icon="icon-message")]
public class EmailAddressPropertyEditor : DataEditor
{
/// <summary>
@@ -19,5 +19,16 @@ namespace Umbraco.Web.PropertyEditors
/// <inheritdoc />
protected override IConfigurationEditor CreateConfigurationEditor() => new TrueFalseConfigurationEditor();
public TrueFalsePreValueEditor()
{
Fields.Add(new PreValueField()
{
Description = "Write a label text",
Key = "labelOn",
Name = "Label",
View = "textstring"
});
}
}
}
@@ -34,6 +34,8 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
private readonly ICacheProvider _cacheProvider; // at snapshot/request level (see PublishedContentCache)
private readonly PublishedContentTypeCache _contentTypeCache;
private readonly object _initializeLock = new object();
private bool _nodeInitialized;
private bool _parentInitialized;
private bool _childrenInitialized;
@@ -66,15 +68,14 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
if (_childrenInitialized == false) InitializeChildren();
EnsureNodeInitialized(andChildren: true);
return _children;
}
}
public override IPublishedProperty GetProperty(string alias)
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
IPublishedProperty property;
return _properties.TryGetValue(alias, out property) ? property : null;
}
@@ -85,8 +86,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
if (_parentInitialized == false) InitializeParent();
EnsureNodeInitialized(andParent: true);
return _parent;
}
}
@@ -95,7 +95,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _id;
}
}
@@ -104,7 +104,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _key;
}
}
@@ -113,7 +113,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _template;
}
}
@@ -122,7 +122,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _sortOrder;
}
}
@@ -131,7 +131,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _name;
}
}
@@ -144,7 +144,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _writerName;
}
}
@@ -153,7 +153,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _creatorName;
}
}
@@ -162,7 +162,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _writerId;
}
}
@@ -171,7 +171,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _creatorId;
}
}
@@ -180,7 +180,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _path;
}
}
@@ -189,7 +189,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _createDate;
}
}
@@ -198,7 +198,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _updateDate;
}
}
@@ -207,7 +207,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _urlName;
}
}
@@ -216,7 +216,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _level;
}
}
@@ -225,7 +225,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _isDraft;
}
}
@@ -234,7 +234,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _properties.Values;
}
}
@@ -243,7 +243,7 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
{
get
{
if (_nodeInitialized == false) InitializeNode();
EnsureNodeInitialized();
return _contentType;
}
}
@@ -256,10 +256,27 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
if (parent.Attributes?.GetNamedItem("isDoc") != null)
_parent = Get(parent, _isPreviewing, _cacheProvider, _contentTypeCache);
// warn: this is not thread-safe...
_parentInitialized = true;
}
private void EnsureNodeInitialized(bool andChildren = false, bool andParent = false)
{
// In *theory* XmlPublishedContent are a per-request thing, and so should not
// end up being involved into multi-threaded situations - however, it's been
// reported that some users ended up seeing 100% CPU due to infinite loops in
// the properties dictionary in InitializeNode, which would indicate that the
// dictionary *is* indeed involved in some multi-threaded operation. No idea
// what users are doing that cause this, but let's be friendly and use a true
// lock around initialization.
lock (_initializeLock)
{
if (_nodeInitialized == false) InitializeNode();
if (andChildren && _childrenInitialized == false) InitializeChildren();
if (andParent && _parentInitialized == false) InitializeParent();
}
}
private void InitializeNode()
{
InitializeNode(this, _xmlNode, _isPreviewing,
@@ -268,10 +285,10 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
out _createDate, out _updateDate, out _level, out _isDraft, out _contentType, out _properties,
_contentTypeCache.Get);
// warn: this is not thread-safe...
_nodeInitialized = true;
}
// internal for some benchmarks
internal static void InitializeNode(XmlPublishedContent node, XmlNode xmlNode, bool isPreviewing,
out int id, out Guid key, out int template, out int sortOrder, out string name, out string writerName, out string urlName,
out string creatorName, out int creatorId, out int writerId, out string docTypeAlias, out int docTypeId, out string path,
@@ -398,7 +415,6 @@ namespace Umbraco.Web.PublishedCache.XmlPublishedCache
.OrderBy(x => x.SortOrder)
.ToList();
// warn: this is not thread-safe
_childrenInitialized = true;
}
+1 -32
View File
@@ -1,6 +1,4 @@
using HtmlAgilityPack;
using System;
using System.Runtime.CompilerServices;
using System;
using System.Text.RegularExpressions;
using Umbraco.Core;
using Umbraco.Core.Configuration;
@@ -38,11 +36,6 @@ namespace Umbraco.Web.Templates
{
if (urlProvider == null) throw new ArgumentNullException("urlProvider");
if(string.IsNullOrEmpty(text))
{
return text;
}
// Parse internal links
var tags = LocalLinkPattern.Matches(text);
foreach (Match tag in tags)
@@ -71,11 +64,6 @@ namespace Umbraco.Web.Templates
}
}
if (UmbracoConfig.For.UmbracoSettings().Content.StripUdiAttributes)
{
text = StripUdiDataAttributes(text);
}
return text;
}
@@ -87,9 +75,6 @@ namespace Umbraco.Web.Templates
private static readonly Regex ResolveUrlPattern = new Regex("(=[\"\']?)(\\W?\\~(?:.(?![\"\']?\\s+(?:\\S+)=|[>\"\']))+.)[\"\']?",
RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace);
private static readonly Regex UdiDataAttributePattern = new Regex("data-udi=\"[^\\\"]*\"",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
/// <summary>
/// The RegEx matches any HTML attribute values that start with a tilde (~), those that match are passed to ResolveUrl to replace the tilde with the application path.
/// </summary>
@@ -133,21 +118,5 @@ namespace Umbraco.Web.Templates
{
return text.CleanForXss(ignoreFromClean);
}
/// <summary>
/// Strips data-udi attributes from rich text
/// </summary>
/// <param name="input">A html string</param>
/// <returns>A string stripped from the data-uid attributes</returns>
public static string StripUdiDataAttributes(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return UdiDataAttributePattern.Replace(input, string.Empty);
}
}
}

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