Merge remote-tracking branch 'origin/v8/dev' into v9/dev
# Conflicts: # build/NuSpecs/UmbracoCms.Web.nuspec # src/SolutionInfo.cs # src/Umbraco.Core/Configuration/UmbracoSettings/ContentElement.cs # src/Umbraco.Core/Configuration/UmbracoSettings/IContentSection.cs # src/Umbraco.Examine/Umbraco.Examine.csproj # src/Umbraco.Tests/Umbraco.Tests.csproj # src/Umbraco.Web.UI.Client/src/assets/img/application/umbraco_logo_large_blue.svg # src/Umbraco.Web.UI.Client/src/assets/img/application/umbraco_logomark_white.svg # src/Umbraco.Web.UI.Client/src/common/directives/components/application/umbappheader.directive.js # src/Umbraco.Web.UI.Client/src/less/components/application/umb-app-header.less # src/Umbraco.Web.UI.Client/src/views/components/application/umb-app-header.html # src/Umbraco.Web.UI.Client/src/views/content/content.protect.controller.js # src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.controller.js # src/Umbraco.Web.UI.Client/src/views/packages/views/install-local.html # src/Umbraco.Web/Editors/BackOfficeServerVariables.cs # src/Umbraco.Web/Umbraco.Web.csproj
This commit is contained in:
@@ -156,6 +156,7 @@ namespace Umbraco.Cms.Core.Configuration.Models
|
||||
internal const bool StaticShowDeprecatedPropertyEditors = false;
|
||||
internal const string StaticLoginBackgroundImage = "assets/img/login.jpg";
|
||||
internal const string StaticLoginLogoImage = "assets/img/application/umbraco_logo_white.svg";
|
||||
internal const bool StaticHideBackOfficeLogo = false;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value for the content notification settings.
|
||||
@@ -219,6 +220,12 @@ namespace Umbraco.Cms.Core.Configuration.Models
|
||||
[DefaultValue(StaticLoginLogoImage)]
|
||||
public string LoginLogoImage { get; set; } = StaticLoginLogoImage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to hide the backoffice umbraco logo or not.
|
||||
/// </summary>
|
||||
[DefaultValue(StaticHideBackOfficeLogo)]
|
||||
public bool HideBackOfficeLogo { get; set; } = StaticHideBackOfficeLogo;
|
||||
|
||||
/// <summary>
|
||||
/// Get or sets the model representing the global content version cleanup policy
|
||||
/// </summary>
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
var keepOnlyKeys = new Dictionary<string, string[]>
|
||||
{
|
||||
{"umbracoUrls", new[] {"authenticationApiBaseUrl", "serverVarsJs", "externalLoginsUrl", "currentUserApiBaseUrl", "previewHubUrl", "iconApiBaseUrl"}},
|
||||
{"umbracoSettings", new[] {"allowPasswordReset", "imageFileTypes", "maxFileSize", "loginBackgroundImage", "loginLogoImage", "canSendRequiredEmail", "usernameIsEmail", "minimumPasswordLength", "minimumPasswordNonAlphaNum"}},
|
||||
{"umbracoSettings", new[] {"allowPasswordReset", "imageFileTypes", "maxFileSize", "loginBackgroundImage", "loginLogoImage", "canSendRequiredEmail", "usernameIsEmail", "minimumPasswordLength", "minimumPasswordNonAlphaNum", "hideBackofficeLogo"}},
|
||||
{"application", new[] {"applicationPath", "cacheBuster"}},
|
||||
{"isDebuggingEnabled", new string[] { }},
|
||||
{"features", new [] {"disabledFeatures"}}
|
||||
@@ -408,6 +408,7 @@ namespace Umbraco.Cms.Web.BackOffice.Controllers
|
||||
{"allowPasswordReset", _securitySettings.AllowPasswordReset},
|
||||
{"loginBackgroundImage", _contentSettings.LoginBackgroundImage},
|
||||
{"loginLogoImage", _contentSettings.LoginLogoImage },
|
||||
{"hideBackofficeLogo", _contentSettings.HideBackOfficeLogo },
|
||||
{"showUserInvite", _emailSender.CanSendRequiredEmail()},
|
||||
{"canSendRequiredEmail", _emailSender.CanSendRequiredEmail()},
|
||||
{"showAllowSegmentationForDocumentTypes", false},
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using Microsoft.AspNetCore.Routing;
|
||||
using Umbraco.Cms.Core.Composing;
|
||||
using Umbraco.Cms.Core.Services;
|
||||
using Umbraco.Cms.Web.Common.Attributes;
|
||||
@@ -11,11 +13,16 @@ namespace Umbraco.Cms.Web.BackOffice.Services
|
||||
public class ConflictingRouteService : IConflictingRouteService
|
||||
{
|
||||
private readonly TypeLoader _typeLoader;
|
||||
private readonly IEnumerable<EndpointDataSource> _endpointDataSources;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConflictingRouteService"/> class.
|
||||
/// </summary>
|
||||
public ConflictingRouteService(TypeLoader typeLoader) => _typeLoader = typeLoader;
|
||||
public ConflictingRouteService(TypeLoader typeLoader, IEnumerable<EndpointDataSource> endpointDataSources)
|
||||
{
|
||||
_typeLoader = typeLoader;
|
||||
_endpointDataSources = endpointDataSources;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool HasConflictingRoutes(out string controllerName)
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 315.89 315.89">
|
||||
<path fill="#ffffff" d="M0,157.74A157.95,157.95,0,1,1,158,315.89,157.95,157.95,0,0,1,0,157.74Zm154.74,54.09a155.41,155.41,0,0,1-36.5-3.29,27.92,27.92,0,0,1-19.94-16q-5.35-12.34-5.21-38.1a243,243,0,0,1,1.69-26.84q1.55-13,3.09-21.46l1.07-5.59a2,2,0,0,0,0-.49,3.2,3.2,0,0,0-2.65-3.17L75.92,93.67h-.44a3.19,3.19,0,0,0-3.11,2.48c-.35,1.31-.56,2.27-1.17,5.38-1.16,6-2.24,11.85-3.43,20.38a264.17,264.17,0,0,0-2.3,27.94,145.24,145.24,0,0,0,0,19.57q.72,25.94,8.9,41.42t27.72,22.3q19.53,6.81,54.43,6.66h2.91q34.94.15,54.41-6.66t27.71-22.3q8.17-15.53,8.91-41.42a145.24,145.24,0,0,0,0-19.57,266.84,266.84,0,0,0-2.3-27.94c-1.2-8.44-2.27-14.26-3.44-20.38-.61-3.11-.81-4.07-1.16-5.38a3.21,3.21,0,0,0-3.12-2.48h-.52l-20.38,3.18a3.2,3.2,0,0,0-2.68,3.17,4,4,0,0,0,0,.49l1.08,5.59q1.55,8.48,3.12,21.46a245.68,245.68,0,0,1,1.65,26.84q.27,25.69-5.21,38.07a27.9,27.9,0,0,1-19.76,16.07,155.19,155.19,0,0,1-36.48,3.29Z"/>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 315.89 315.89">
|
||||
<path fill="#ffffff" d="M0,157.74A157.95,157.95,0,1,1,158,315.89,157.95,157.95,0,0,1,0,157.74Zm154.74,54.09a155.41,155.41,0,0,1-36.5-3.29,27.92,27.92,0,0,1-19.94-16q-5.35-12.34-5.21-38.1a243,243,0,0,1,1.69-26.84q1.55-13,3.09-21.46l1.07-5.59a2,2,0,0,0,0-.49,3.2,3.2,0,0,0-2.65-3.17L75.92,93.67h-.44a3.19,3.19,0,0,0-3.11,2.48c-.35,1.31-.56,2.27-1.17,5.38-1.16,6-2.24,11.85-3.43,20.38a264.17,264.17,0,0,0-2.3,27.94,145.24,145.24,0,0,0,0,19.57q.72,25.94,8.9,41.42t27.72,22.3q19.53,6.81,54.43,6.66h2.91q34.94.15,54.41-6.66t27.71-22.3q8.17-15.53,8.91-41.42a145.24,145.24,0,0,0,0-19.57,266.84,266.84,0,0,0-2.3-27.94c-1.2-8.44-2.27-14.26-3.44-20.38-.61-3.11-.81-4.07-1.16-5.38a3.21,3.21,0,0,0-3.12-2.48h-.52l-20.38,3.18a3.2,3.2,0,0,0-2.68,3.17,4,4,0,0,0,0,.49l1.08,5.59q1.55,8.48,3.12,21.46a245.68,245.68,0,0,1,1.65,26.84q.27,25.69-5.21,38.07a27.9,27.9,0,0,1-19.76,16.07,155.19,155.19,0,0,1-36.48,3.29Z"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 976 B After Width: | Height: | Size: 979 B |
@@ -16,6 +16,7 @@
|
||||
{ value: "assets/img/application/logo@2x.png" },
|
||||
{ value: "assets/img/application/logo@3x.png" }
|
||||
];
|
||||
scope.hideBackofficeLogo = Umbraco.Sys.ServerVariables.umbracoSettings.hideBackofficeLogo;
|
||||
|
||||
// when a user logs out or timesout
|
||||
evts.push(eventsService.on("app.notAuthenticated", function () {
|
||||
@@ -104,15 +105,26 @@
|
||||
$timeout.cancel(scope.logoModal.timer);
|
||||
};
|
||||
scope.hideLogoModal = function() {
|
||||
$timeout.cancel(scope.logoModal.timer);
|
||||
scope.logoModal.timer = $timeout(function () {
|
||||
scope.logoModal.show = false;
|
||||
}, 100);
|
||||
if(scope.logoModal.show === true) {
|
||||
$timeout.cancel(scope.logoModal.timer);
|
||||
scope.logoModal.timer = $timeout(function () {
|
||||
scope.logoModal.show = false;
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
scope.stopClickEvent = function($event) {
|
||||
$event.stopPropagation();
|
||||
};
|
||||
|
||||
scope.toggleLogoModal = function() {
|
||||
if(scope.logoModal.show) {
|
||||
$timeout.cancel(scope.logoModal.timer);
|
||||
scope.logoModal.show = false;
|
||||
} else {
|
||||
scope.showLogoModal();
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
var directive = {
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* @ngdoc filter
|
||||
* @name umbraco.filters.simpleMarkdown
|
||||
* @description
|
||||
* Used when rendering a string as Markdown as HTML (i.e. with ng-bind-html). Allows use of **bold**, *italics*,  and [links](url)
|
||||
**/
|
||||
angular.module("umbraco.filters").filter('simpleMarkdown', function () {
|
||||
return function (text) {
|
||||
if (!text) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return text
|
||||
.replace(/\*\*(.*)\*\*/gim, '<b>$1</b>')
|
||||
.replace(/\*(.*)\*/gim, '<i>$1</i>')
|
||||
.replace(/!\[(.*?)\]\((.*?)\)/gim, "<img alt='$1' src='$2' />")
|
||||
.replace(/\[(.*?)\]\((.*?)\)/gim, "<a href='$2' target='_blank' rel='noopener' class='underline'>$1</a>")
|
||||
.replace(/\n/g, '<br />').trim();
|
||||
};
|
||||
});
|
||||
@@ -30,9 +30,8 @@
|
||||
|
||||
for (var p = 0; p < tab.properties.length; p++) {
|
||||
var prop = tab.properties[p];
|
||||
if (dataModel[prop.alias]) {
|
||||
prop.value = dataModel[prop.alias];
|
||||
}
|
||||
|
||||
prop.value = dataModel[prop.alias];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,9 +52,8 @@
|
||||
|
||||
for (var p = 0; p < tab.properties.length; p++) {
|
||||
var prop = tab.properties[p];
|
||||
if (prop.value) {
|
||||
dataModel[prop.alias] = prop.value;
|
||||
}
|
||||
|
||||
dataModel[prop.alias] = prop.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -259,8 +259,8 @@ function navigationService($routeParams, $location, $q, $injector, eventsService
|
||||
var updated = false;
|
||||
|
||||
retainedQueryStrings.forEach(r => {
|
||||
// if mculture is set to null in nextRouteParams, the value will be undefined and we will not retain any query string that has a value of "null"
|
||||
if (currRouteParams[r] && nextRouteParams[r] !== undefined && !nextRouteParams[r]) {
|
||||
// testing explicitly for undefined in nextRouteParams here, as it must be possible to "unset" e.g. mculture by specifying a null value
|
||||
if (currRouteParams[r] && nextRouteParams[r] === undefined) {
|
||||
toRetain[r] = currRouteParams[r];
|
||||
updated = true;
|
||||
}
|
||||
|
||||
@@ -9,11 +9,18 @@
|
||||
|
||||
.umb-app-header__logo {
|
||||
margin-right: 30px;
|
||||
flex-shrink: 0;
|
||||
button {
|
||||
img {
|
||||
height: 30px;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@media (max-width: 1279px) {
|
||||
.umb-app-header__logo {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-app-header__logo-modal {
|
||||
|
||||
@@ -9,44 +9,51 @@
|
||||
color: @gray-5;
|
||||
}
|
||||
|
||||
.umb-upload-local__dropzone {
|
||||
position: relative;
|
||||
width: 500px;
|
||||
height: 300px;
|
||||
border: 2px dashed @ui-action-border;
|
||||
border-radius: 3px;
|
||||
background: @white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
transition: 100ms box-shadow ease, 100ms border ease;
|
||||
.umb-upload-local {
|
||||
|
||||
&.drag-over {
|
||||
border-color: @ui-action-border-hover;
|
||||
border-style: solid;
|
||||
box-shadow: 0 3px 8px rgba(0,0,0, .1);
|
||||
&__dropzone {
|
||||
position: relative;
|
||||
width: 500px;
|
||||
height: 300px;
|
||||
border: 2px dashed @ui-action-border;
|
||||
border-radius: 3px;
|
||||
background: @white;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-bottom: 30px;
|
||||
transition: 100ms box-shadow ease, 100ms border ease;
|
||||
|
||||
&.drag-over {
|
||||
border-color: @ui-action-border-hover;
|
||||
border-style: solid;
|
||||
box-shadow: 0 3px 8px rgba(0,0,0, .1);
|
||||
transition: 100ms box-shadow ease, 100ms border ease;
|
||||
}
|
||||
|
||||
.umb-icon {
|
||||
display: block;
|
||||
color: @ui-action-type;
|
||||
font-size: 6.75rem;
|
||||
line-height: 1;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.umb-info-local-item {
|
||||
margin: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.umb-icon {
|
||||
display: block;
|
||||
&__select-file {
|
||||
font-weight: bold;
|
||||
color: @ui-action-type;
|
||||
font-size: 6.75rem;
|
||||
line-height: 1;
|
||||
margin: 0 auto;
|
||||
}
|
||||
}
|
||||
cursor: pointer;
|
||||
|
||||
.umb-upload-local__select-file {
|
||||
font-weight: bold;
|
||||
color: @ui-action-type;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: @ui-action-type-hover;
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
color: @ui-action-type-hover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,7 +124,3 @@
|
||||
.umb-info-local-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.umb-upload-local__dropzone .umb-info-local-item {
|
||||
margin:20px;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
<div>
|
||||
<div class="umb-app-header">
|
||||
<div class="umb-app-header__logo">
|
||||
<button type="button" class="btn-reset" ng-click="showLogoModal()">
|
||||
<div class="umb-app-header__logo" ng-if="hideBackofficeLogo !== true">
|
||||
<button
|
||||
type="button"
|
||||
class="btn-reset"
|
||||
ng-click="toggleLogoModal()"
|
||||
>
|
||||
<img
|
||||
src="assets/img/application/umbraco_logomark_white.svg"
|
||||
alt="Umbraco"
|
||||
|
||||
@@ -1,261 +1,261 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
"use strict";
|
||||
|
||||
function ContentProtectController($scope, $q, publicAccessResource, memberResource, memberGroupResource, navigationService, localizationService, editorService) {
|
||||
function ContentProtectController($scope, $q, contentResource, memberResource, memberGroupResource, navigationService, localizationService, editorService) {
|
||||
|
||||
var vm = this;
|
||||
var id = $scope.currentNode.id;
|
||||
var vm = this;
|
||||
var id = $scope.currentNode.id;
|
||||
|
||||
vm.loading = false;
|
||||
vm.buttonState = "init";
|
||||
|
||||
vm.isValid = isValid;
|
||||
vm.next = next;
|
||||
vm.save = save;
|
||||
vm.close = close;
|
||||
vm.toggle = toggle;
|
||||
vm.pickLoginPage = pickLoginPage;
|
||||
vm.pickErrorPage = pickErrorPage;
|
||||
vm.pickGroup = pickGroup;
|
||||
vm.removeGroup = removeGroup;
|
||||
vm.pickMember = pickMember;
|
||||
vm.removeMember = removeMember;
|
||||
vm.removeProtection = removeProtection;
|
||||
vm.removeProtectionConfirm = removeProtectionConfirm;
|
||||
|
||||
vm.type = null;
|
||||
vm.step = null;
|
||||
|
||||
function onInit() {
|
||||
vm.loading = true;
|
||||
|
||||
// get the current public access protection
|
||||
publicAccessResource.getPublicAccess(id).then(function (publicAccess) {
|
||||
vm.loading = false;
|
||||
vm.buttonState = "init";
|
||||
|
||||
// init the current settings for public access (if any)
|
||||
vm.loginPage = publicAccess.loginPage;
|
||||
vm.errorPage = publicAccess.errorPage;
|
||||
vm.groups = publicAccess.groups || [];
|
||||
vm.members = publicAccess.members || [];
|
||||
vm.canRemove = true;
|
||||
vm.isValid = isValid;
|
||||
vm.next = next;
|
||||
vm.save = save;
|
||||
vm.close = close;
|
||||
vm.toggle = toggle;
|
||||
vm.pickLoginPage = pickLoginPage;
|
||||
vm.pickErrorPage = pickErrorPage;
|
||||
vm.pickGroup = pickGroup;
|
||||
vm.removeGroup = removeGroup;
|
||||
vm.pickMember = pickMember;
|
||||
vm.removeMember = removeMember;
|
||||
vm.removeProtection = removeProtection;
|
||||
vm.removeProtectionConfirm = removeProtectionConfirm;
|
||||
|
||||
if (vm.members.length) {
|
||||
vm.type = "member";
|
||||
next();
|
||||
}
|
||||
else if (vm.groups.length) {
|
||||
vm.type = "group";
|
||||
next();
|
||||
}
|
||||
else {
|
||||
vm.canRemove = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
vm.type = null;
|
||||
vm.step = null;
|
||||
|
||||
function next() {
|
||||
if (vm.type === "group") {
|
||||
vm.loading = true;
|
||||
// get all existing member groups for lookup upon selection
|
||||
// NOTE: if/when member groups support infinite editing, we can't rely on using a cached lookup list of valid groups anymore
|
||||
memberGroupResource.getGroups().then(function (groups) {
|
||||
vm.step = vm.type;
|
||||
vm.allGroups = groups;
|
||||
vm.hasGroups = groups.length > 0;
|
||||
vm.loading = false;
|
||||
});
|
||||
}
|
||||
else {
|
||||
vm.step = vm.type;
|
||||
}
|
||||
}
|
||||
|
||||
function isValid() {
|
||||
if (!vm.type) {
|
||||
return false;
|
||||
}
|
||||
if (!vm.protectForm.$valid) {
|
||||
return false;
|
||||
}
|
||||
if (!vm.loginPage || !vm.errorPage) {
|
||||
return false;
|
||||
}
|
||||
if (vm.type === "group") {
|
||||
return vm.groups && vm.groups.length > 0;
|
||||
}
|
||||
if (vm.type === "member") {
|
||||
return vm.members && vm.members.length > 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function save() {
|
||||
vm.buttonState = "busy";
|
||||
var groups = _.map(vm.groups, function (group) { return group.name; });
|
||||
var usernames = _.map(vm.members, function (member) { return member.username; });
|
||||
publicAccessResource.updatePublicAccess(id, groups, usernames, vm.loginPage.id, vm.errorPage.id).then(
|
||||
function () {
|
||||
localizationService.localize("publicAccess_paIsProtected", [$scope.currentNode.name]).then(function (value) {
|
||||
vm.success = {
|
||||
message: value
|
||||
};
|
||||
});
|
||||
navigationService.syncTree({ tree: "content", path: $scope.currentNode.path, forceReload: true });
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
}, function (error) {
|
||||
vm.error = error;
|
||||
vm.buttonState = "error";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function close() {
|
||||
// ensure that we haven't set a locked state on the dialog before closing it
|
||||
navigationService.allowHideDialog(true);
|
||||
navigationService.hideDialog();
|
||||
}
|
||||
|
||||
function toggle(group) {
|
||||
group.selected = !group.selected;
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
}
|
||||
|
||||
function pickGroup() {
|
||||
navigationService.allowHideDialog(false);
|
||||
editorService.memberGroupPicker({
|
||||
multiPicker: true,
|
||||
submit: function (model) {
|
||||
var selectedGroupIds = model.selectedMemberGroups
|
||||
? model.selectedMemberGroups
|
||||
: [model.selectedMemberGroup];
|
||||
_.each(selectedGroupIds,
|
||||
function (groupId) {
|
||||
// find the group in the lookup list and add it if it isn't already
|
||||
var group = _.find(vm.allGroups, function (g) { return g.id === parseInt(groupId); });
|
||||
if (group && !_.find(vm.groups, function (g) { return g.id === group.id })) {
|
||||
vm.groups.push(group);
|
||||
}
|
||||
});
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeGroup(group) {
|
||||
vm.groups = _.reject(vm.groups, function (g) { return g.id === group.id });
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
}
|
||||
|
||||
function pickMember() {
|
||||
navigationService.allowHideDialog(false);
|
||||
// TODO: once editorService has a memberPicker method, use that instead
|
||||
editorService.treePicker({
|
||||
multiPicker: true,
|
||||
entityType: "Member",
|
||||
section: "member",
|
||||
treeAlias: "member",
|
||||
filter: function (i) {
|
||||
return i.metaData.isContainer;
|
||||
},
|
||||
filterCssClass: "not-allowed",
|
||||
submit: function (model) {
|
||||
if (model.selection && model.selection.length) {
|
||||
var promises = [];
|
||||
// get the selected member usernames
|
||||
_.each(model.selection,
|
||||
function (member) {
|
||||
// TODO:
|
||||
// as-is we need to fetch all the picked members one at a time to get their usernames.
|
||||
// when editorService has a memberPicker method, see if this can't be avoided - otherwise
|
||||
// add a memberResource.getByKeys() method to do all this in one request
|
||||
promises.push(
|
||||
memberResource.getByKey(member.key).then(function (newMember) {
|
||||
if (!_.find(vm.members, function (currentMember) { return currentMember.username === newMember.username })) {
|
||||
vm.members.push(newMember);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
// wait for all the member lookups to complete
|
||||
function onInit() {
|
||||
vm.loading = true;
|
||||
$q.all(promises).then(function () {
|
||||
vm.loading = false;
|
||||
|
||||
// get the current public access protection
|
||||
contentResource.getPublicAccess(id).then(function (publicAccess) {
|
||||
vm.loading = false;
|
||||
|
||||
// init the current settings for public access (if any)
|
||||
vm.loginPage = publicAccess.loginPage;
|
||||
vm.errorPage = publicAccess.errorPage;
|
||||
vm.groups = publicAccess.groups || [];
|
||||
vm.members = publicAccess.members || [];
|
||||
vm.canRemove = true;
|
||||
|
||||
if (vm.members.length) {
|
||||
vm.type = "member";
|
||||
next();
|
||||
}
|
||||
else if (vm.groups.length) {
|
||||
vm.type = "group";
|
||||
next();
|
||||
}
|
||||
else {
|
||||
vm.canRemove = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function next() {
|
||||
if (vm.type === "group") {
|
||||
vm.loading = true;
|
||||
// get all existing member groups for lookup upon selection
|
||||
// NOTE: if/when member groups support infinite editing, we can't rely on using a cached lookup list of valid groups anymore
|
||||
memberGroupResource.getGroups().then(function (groups) {
|
||||
vm.step = vm.type;
|
||||
vm.allGroups = groups;
|
||||
vm.hasGroups = groups.length > 0;
|
||||
vm.loading = false;
|
||||
});
|
||||
}
|
||||
else {
|
||||
vm.step = vm.type;
|
||||
}
|
||||
}
|
||||
|
||||
function isValid() {
|
||||
if (!vm.type) {
|
||||
return false;
|
||||
}
|
||||
if (!vm.protectForm.$valid) {
|
||||
return false;
|
||||
}
|
||||
if (!vm.loginPage || !vm.errorPage) {
|
||||
return false;
|
||||
}
|
||||
if (vm.type === "group") {
|
||||
return vm.groups && vm.groups.length > 0;
|
||||
}
|
||||
if (vm.type === "member") {
|
||||
return vm.members && vm.members.length > 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function save() {
|
||||
vm.buttonState = "busy";
|
||||
var groups = _.map(vm.groups, function (group) { return encodeURIComponent(group.name); });
|
||||
var usernames = _.map(vm.members, function (member) { return member.username; });
|
||||
contentResource.updatePublicAccess(id, groups, usernames, vm.loginPage.id, vm.errorPage.id).then(
|
||||
function () {
|
||||
localizationService.localize("publicAccess_paIsProtected", [$scope.currentNode.name]).then(function (value) {
|
||||
vm.success = {
|
||||
message: value
|
||||
};
|
||||
});
|
||||
navigationService.syncTree({ tree: "content", path: $scope.currentNode.path, forceReload: true });
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
}, function (error) {
|
||||
vm.error = error;
|
||||
vm.buttonState = "error";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function close() {
|
||||
// ensure that we haven't set a locked state on the dialog before closing it
|
||||
navigationService.allowHideDialog(true);
|
||||
navigationService.hideDialog();
|
||||
}
|
||||
|
||||
function toggle(group) {
|
||||
group.selected = !group.selected;
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
}
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeMember(member) {
|
||||
vm.members = _.without(vm.members, member);
|
||||
}
|
||||
|
||||
function pickLoginPage() {
|
||||
pickPage(vm.loginPage);
|
||||
}
|
||||
|
||||
function pickErrorPage() {
|
||||
pickPage(vm.errorPage);
|
||||
}
|
||||
|
||||
function pickPage(page) {
|
||||
navigationService.allowHideDialog(false);
|
||||
editorService.contentPicker({
|
||||
submit: function (model) {
|
||||
if (page === vm.loginPage) {
|
||||
vm.loginPage = model.selection[0];
|
||||
}
|
||||
else {
|
||||
vm.errorPage = model.selection[0];
|
||||
}
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
function pickGroup() {
|
||||
navigationService.allowHideDialog(false);
|
||||
editorService.memberGroupPicker({
|
||||
multiPicker: true,
|
||||
submit: function(model) {
|
||||
var selectedGroupIds = model.selectedMemberGroups
|
||||
? model.selectedMemberGroups
|
||||
: [model.selectedMemberGroup];
|
||||
_.each(selectedGroupIds,
|
||||
function (groupId) {
|
||||
// find the group in the lookup list and add it if it isn't already
|
||||
var group = _.find(vm.allGroups, function(g) { return g.id === parseInt(groupId); });
|
||||
if (group && !_.find(vm.groups, function (g) { return g.id === group.id })) {
|
||||
vm.groups.push(group);
|
||||
}
|
||||
});
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
},
|
||||
close: function() {
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeProtection() {
|
||||
vm.removing = true;
|
||||
}
|
||||
|
||||
function removeProtectionConfirm() {
|
||||
vm.buttonState = "busy";
|
||||
publicAccessResource.removePublicAccess(id).then(
|
||||
function () {
|
||||
localizationService.localize("publicAccess_paIsRemoved", [$scope.currentNode.name]).then(function (value) {
|
||||
vm.success = {
|
||||
message: value
|
||||
};
|
||||
});
|
||||
navigationService.syncTree({ tree: "content", path: $scope.currentNode.path, forceReload: true });
|
||||
}, function (error) {
|
||||
vm.error = error;
|
||||
vm.buttonState = "error";
|
||||
function removeGroup(group) {
|
||||
vm.groups = _.reject(vm.groups, function(g) { return g.id === group.id });
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
}
|
||||
);
|
||||
|
||||
function pickMember() {
|
||||
navigationService.allowHideDialog(false);
|
||||
// TODO: once editorService has a memberPicker method, use that instead
|
||||
editorService.treePicker({
|
||||
multiPicker: true,
|
||||
entityType: "Member",
|
||||
section: "member",
|
||||
treeAlias: "member",
|
||||
filter: function (i) {
|
||||
return i.metaData.isContainer;
|
||||
},
|
||||
filterCssClass: "not-allowed",
|
||||
submit: function (model) {
|
||||
if (model.selection && model.selection.length) {
|
||||
var promises = [];
|
||||
// get the selected member usernames
|
||||
_.each(model.selection,
|
||||
function (member) {
|
||||
// TODO:
|
||||
// as-is we need to fetch all the picked members one at a time to get their usernames.
|
||||
// when editorService has a memberPicker method, see if this can't be avoided - otherwise
|
||||
// add a memberResource.getByKeys() method to do all this in one request
|
||||
promises.push(
|
||||
memberResource.getByKey(member.key).then(function(newMember) {
|
||||
if (!_.find(vm.members, function (currentMember) { return currentMember.username === newMember.username })) {
|
||||
vm.members.push(newMember);
|
||||
}
|
||||
})
|
||||
);
|
||||
});
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
// wait for all the member lookups to complete
|
||||
vm.loading = true;
|
||||
$q.all(promises).then(function() {
|
||||
vm.loading = false;
|
||||
});
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
}
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeMember(member) {
|
||||
vm.members = _.without(vm.members, member);
|
||||
}
|
||||
|
||||
function pickLoginPage() {
|
||||
pickPage(vm.loginPage);
|
||||
}
|
||||
|
||||
function pickErrorPage() {
|
||||
pickPage(vm.errorPage);
|
||||
}
|
||||
|
||||
function pickPage(page) {
|
||||
navigationService.allowHideDialog(false);
|
||||
editorService.contentPicker({
|
||||
submit: function (model) {
|
||||
if (page === vm.loginPage) {
|
||||
vm.loginPage = model.selection[0];
|
||||
}
|
||||
else {
|
||||
vm.errorPage = model.selection[0];
|
||||
}
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
$scope.dialog.confirmDiscardChanges = true;
|
||||
},
|
||||
close: function () {
|
||||
editorService.close();
|
||||
navigationService.allowHideDialog(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function removeProtection() {
|
||||
vm.removing = true;
|
||||
}
|
||||
|
||||
function removeProtectionConfirm() {
|
||||
vm.buttonState = "busy";
|
||||
contentResource.removePublicAccess(id).then(
|
||||
function () {
|
||||
localizationService.localize("publicAccess_paIsRemoved", [$scope.currentNode.name]).then(function(value) {
|
||||
vm.success = {
|
||||
message: value
|
||||
};
|
||||
});
|
||||
navigationService.syncTree({ tree: "content", path: $scope.currentNode.path, forceReload: true });
|
||||
}, function (error) {
|
||||
vm.error = error;
|
||||
vm.buttonState = "error";
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
onInit();
|
||||
}
|
||||
|
||||
onInit();
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Content.ProtectController", ContentProtectController);
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Content.ProtectController", ContentProtectController);
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,212 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
function PackagesInstallLocalController($scope, Upload, umbRequestHelper, packageResource, localStorageService, $timeout, $window, localizationService, $q) {
|
||||
|
||||
var vm = this;
|
||||
vm.state = "upload";
|
||||
|
||||
vm.localPackage = {};
|
||||
vm.installPackage = installPackage;
|
||||
vm.installState = {
|
||||
status: "",
|
||||
progress: 0
|
||||
};
|
||||
vm.installCompleted = false;
|
||||
vm.zipFile = {
|
||||
uploadStatus: "idle",
|
||||
uploadProgress: 0,
|
||||
serverErrorMessage: null
|
||||
};
|
||||
|
||||
$scope.handleFiles = function (files, event, invalidFiles) {
|
||||
if (files) {
|
||||
for (var i = 0; i < files.length; i++) {
|
||||
upload(files[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var labels = {};
|
||||
var labelKeys = [
|
||||
"packager_installStateImporting",
|
||||
"packager_installStateInstalling",
|
||||
"packager_installStateRestarting",
|
||||
"packager_installStateComplete",
|
||||
"packager_installStateCompleted"
|
||||
];
|
||||
|
||||
localizationService.localizeMany(labelKeys).then(function (values) {
|
||||
labels.installStateImporting = values[0];
|
||||
labels.installStateInstalling = values[1];
|
||||
labels.installStateRestarting = values[2];
|
||||
labels.installStateComplete = values[3];
|
||||
labels.installStateCompleted = values[4];
|
||||
});
|
||||
|
||||
function upload(file) {
|
||||
|
||||
Upload.upload({
|
||||
url: umbRequestHelper.getApiUrl("packageInstallApiBaseUrl", "UploadLocalPackage"),
|
||||
fields: {},
|
||||
file: file
|
||||
}).progress(function (evt) {
|
||||
|
||||
// hack: in some browsers the progress event is called after success
|
||||
// this prevents the UI from going back to a uploading state
|
||||
if (vm.zipFile.uploadStatus !== "done" && vm.zipFile.uploadStatus !== "error") {
|
||||
|
||||
// set view state to uploading
|
||||
vm.state = 'uploading';
|
||||
|
||||
// calculate progress in percentage
|
||||
var progressPercentage = parseInt(100.0 * evt.loaded / evt.total, 10);
|
||||
|
||||
// set percentage property on file
|
||||
vm.zipFile.uploadProgress = progressPercentage;
|
||||
|
||||
// set uploading status on file
|
||||
vm.zipFile.uploadStatus = "uploading";
|
||||
|
||||
}
|
||||
|
||||
}).success(function (data, status, headers, config) {
|
||||
|
||||
if (data.notifications && data.notifications.length > 0) {
|
||||
|
||||
// set error status on file
|
||||
vm.zipFile.uploadStatus = "error";
|
||||
|
||||
// Throw message back to user with the cause of the error
|
||||
vm.zipFile.serverErrorMessage = data.notifications[0].message;
|
||||
|
||||
} else {
|
||||
|
||||
// set done status on file
|
||||
vm.zipFile.uploadStatus = "done";
|
||||
loadPackage();
|
||||
vm.zipFile.uploadProgress = 100;
|
||||
vm.localPackage = data;
|
||||
}
|
||||
|
||||
}).error(function (evt, status, headers, config) {
|
||||
|
||||
// set status done
|
||||
vm.zipFile.uploadStatus = "error";
|
||||
|
||||
// If file not found, server will return a 404 and display this message
|
||||
if (status === 404) {
|
||||
vm.zipFile.serverErrorMessage = "File not found";
|
||||
}
|
||||
else if (status == 400) {
|
||||
//it's a validation error
|
||||
vm.zipFile.serverErrorMessage = evt.message;
|
||||
}
|
||||
else {
|
||||
//it's an unhandled error
|
||||
//if the service returns a detailed error
|
||||
if (evt.InnerException) {
|
||||
vm.zipFile.serverErrorMessage = evt.InnerException.ExceptionMessage;
|
||||
|
||||
//Check if its the common "too large file" exception
|
||||
if (evt.InnerException.StackTrace && evt.InnerException.StackTrace.indexOf("ValidateRequestEntityLength") > 0) {
|
||||
vm.zipFile.serverErrorMessage = "File too large to upload";
|
||||
}
|
||||
|
||||
} else if (evt.Message) {
|
||||
vm.zipFile.serverErrorMessage = evt.Message;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadPackage() {
|
||||
if (vm.zipFile.uploadStatus === "done") {
|
||||
vm.state = "packageDetails";
|
||||
}
|
||||
}
|
||||
|
||||
function installPackage() {
|
||||
|
||||
vm.installState.status = labels.installStateImporting;
|
||||
vm.installState.progress = "0";
|
||||
|
||||
packageResource
|
||||
.import(vm.localPackage)
|
||||
.then(function (pack) {
|
||||
vm.installState.progress = "25";
|
||||
vm.installState.status = labels.installStateInstalling;
|
||||
return packageResource.installFiles(pack);
|
||||
},
|
||||
installError)
|
||||
.then(function (pack) {
|
||||
vm.installState.status = labels.installStateRestarting;
|
||||
vm.installState.progress = "50";
|
||||
var deferred = $q.defer();
|
||||
|
||||
//check if the app domain is restarted ever 2 seconds
|
||||
var count = 0;
|
||||
|
||||
function checkRestart() {
|
||||
$timeout(function () {
|
||||
packageResource.checkRestart(pack).then(function (d) {
|
||||
count++;
|
||||
//if there is an id it means it's not restarted yet but we'll limit it to only check 10 times
|
||||
if (d.isRestarting && count < 10) {
|
||||
checkRestart();
|
||||
}
|
||||
else {
|
||||
//it's restarted!
|
||||
deferred.resolve(d);
|
||||
}
|
||||
},
|
||||
installError);
|
||||
},
|
||||
2000);
|
||||
}
|
||||
|
||||
checkRestart();
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
installError)
|
||||
.then(function (pack) {
|
||||
vm.installState.status = labels.installStateInstalling;
|
||||
vm.installState.progress = "75";
|
||||
return packageResource.installData(pack);
|
||||
},
|
||||
installError)
|
||||
.then(function (pack) {
|
||||
vm.installState.status = labels.installStateComplete;
|
||||
vm.installState.progress = "100";
|
||||
return packageResource.cleanUp(pack);
|
||||
},
|
||||
installError)
|
||||
.then(function (result) {
|
||||
|
||||
//Put the package data in local storage so we can use after reloading
|
||||
localStorageService.set("packageInstallData", result);
|
||||
|
||||
vm.installState.status = labels.installStateCompleted;
|
||||
vm.installCompleted = true;
|
||||
|
||||
|
||||
}, installError);
|
||||
}
|
||||
|
||||
function installError() {
|
||||
//This will return a rejection meaning that the promise change above will stop
|
||||
return $q.reject();
|
||||
}
|
||||
|
||||
vm.reloadPage = function () {
|
||||
//reload on next digest (after cookie)
|
||||
$timeout(function () {
|
||||
$window.location.reload(true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
angular.module("umbraco").controller("Umbraco.Editors.Packages.InstallLocalController", PackagesInstallLocalController);
|
||||
|
||||
})();
|
||||
@@ -0,0 +1,194 @@
|
||||
<div ng-controller="Umbraco.Editors.Packages.InstallLocalController as vm">
|
||||
|
||||
<div class="umb-packages-view-wrapper" ng-if="vm.state === 'upload'">
|
||||
|
||||
<!-- Upload -->
|
||||
<div class="flex items-center justify-center">
|
||||
<ng-form novalidate name="localPackageForm" class="flex flex-column justify-center items-center tc">
|
||||
|
||||
<!-- Drag and drop files area -->
|
||||
<div ngf-drop
|
||||
ng-hide="hideDropzone"
|
||||
ng-model="filesHolder"
|
||||
ngf-change="handleFiles($files, $event, $invalidFiles)"
|
||||
class="umb-upload-local__dropzone"
|
||||
ngf-drag-over-class="'drag-over'"
|
||||
ngf-multiple="false"
|
||||
ngf-allow-dir="false"
|
||||
ngf-max-size="{{maxFileSize}}"
|
||||
ngf-pattern="'.zip'"
|
||||
accept=".zip"
|
||||
ng-class="{ 'is-small': compact !== 'false' || (done.length + queue.length) > 0 }">
|
||||
|
||||
<div class="content" draggable="false">
|
||||
|
||||
<!-- Drag and drop illustration -->
|
||||
<umb-icon icon="icon-box"></umb-icon>
|
||||
<small class="faded">
|
||||
<strong><localize key="packager_dropHere">Drop to upload</localize></strong>
|
||||
</small>
|
||||
|
||||
<!-- Select files -->
|
||||
<div class="umb-upload-local__select-file"
|
||||
ngf-select
|
||||
ng-model="filesHolder"
|
||||
ngf-change="handleFiles($files, $event, $invalidFiles)"
|
||||
ngf-multiple="true"
|
||||
ngf-max-size="{{maxFileSize}}"
|
||||
ngf-pattern="'.zip'"
|
||||
accept=".zip">
|
||||
- <localize key="packager_orClickHereToUpload">or click here to choose files</localize>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<h3><strong><localize key="packager_uploadPackage">Upload package</localize></strong></h3>
|
||||
<p class="faded">
|
||||
<localize key="packager_localPackageDescription">Install a local package by selecting it from your machine. Only install packages from sources you know and trust</localize>.
|
||||
</p>
|
||||
|
||||
</ng-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.state === 'uploading'">
|
||||
|
||||
<umb-editor-sub-header>
|
||||
<umb-editor-sub-header-content-left>
|
||||
<button type="button" class="umb-package-details__back-action" ng-click="vm.state = 'upload'" prevent-default><span aria-hidden="true">←</span> <localize key="packager_uploadAnother">Upload another package</localize></button>
|
||||
</umb-editor-sub-header-content-left>
|
||||
</umb-editor-sub-header>
|
||||
|
||||
<div class="umb-packages-view-wrapper">
|
||||
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="umb-info-local-items">
|
||||
<div class="umb-package-icon">
|
||||
<umb-icon icon="icon-box" ng-if="!vm.localPackage.iconUrl"></umb-icon>
|
||||
</div>
|
||||
<div class="umb-package-info">
|
||||
|
||||
<h4 class="umb-info-local-item"><strong><localize key="packager_installStateUploading">Uploading package...</localize></strong></h4>
|
||||
|
||||
<umb-progress-bar
|
||||
percentage="{{vm.zipFile.uploadProgress}}">
|
||||
</umb-progress-bar>
|
||||
|
||||
<div class="umb-info-local-item text-error mt3" ng-if="vm.zipFile.uploadStatus === 'error'">
|
||||
{{ vm.zipFile.serverErrorMessage }}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-if="vm.state === 'packageDetails'">
|
||||
|
||||
<umb-editor-sub-header>
|
||||
<umb-editor-sub-header-content-left>
|
||||
<button type="button" class="umb-package-details__back-action" ng-click="vm.state = 'upload'" prevent-default><span aria-hidden="true">←</span> <localize key="packager_cancelAndUploadAnother">Cancel and upload another package</localize></button>
|
||||
</umb-editor-sub-header-content-left>
|
||||
</umb-editor-sub-header>
|
||||
|
||||
<div class="umb-packages-view-wrapper">
|
||||
|
||||
<!-- Package details -->
|
||||
<div class="flex items-center justify-center">
|
||||
<div class="umb-info-local-items">
|
||||
|
||||
<form novalidate name="localPackageForm" class="w-100">
|
||||
<div class="umb-package-icon">
|
||||
<umb-icon ng-if="!vm.localPackage.iconUrl" icon="icon-box"></umb-icon>
|
||||
<img ng-if="vm.localPackage.iconUrl" ng-src="{{vm.localPackage.iconUrl}}" alt="" />
|
||||
</div>
|
||||
|
||||
<div class="umb-package-info">
|
||||
<h4 class="umb-info-local-item"><strong>{{ vm.localPackage.name }}</strong></h4>
|
||||
|
||||
<div class="umb-info-local-item">
|
||||
<strong><localize key="packager_packageAuthor">Author</localize></strong><br>
|
||||
<a href="{{ vm.localPackage.authorUrl }}" target="_blank" rel="noopener">{{ vm.localPackage.author }}</a>
|
||||
</div>
|
||||
|
||||
<div class="umb-info-local-item" ng-if="vm.localPackage.contributors && vm.localPackage.contributors.length > 0">
|
||||
<strong><localize key="packager_packageContrib">Contributors</localize></strong><br>
|
||||
<span>{{ vm.localPackage.contributors.join(', ')}}</span>
|
||||
</div>
|
||||
|
||||
<div class="umb-info-local-item">
|
||||
<strong><localize key="packager_packageVersion">Version</localize></strong><br>
|
||||
{{ vm.localPackage.version }}
|
||||
<p ng-if="vm.localPackage.originalVersion">
|
||||
<small>
|
||||
<em>
|
||||
<localize key="packager_packageVersionUpgrade">Upgrading from version</localize>
|
||||
{{ vm.localPackage.originalVersion }}
|
||||
</em>
|
||||
</small>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="umb-info-local-item">
|
||||
<strong><localize key="packager_packageLicense">License</localize></strong><br>
|
||||
<a href="{{ vm.localPackage.licenseUrl }}" target="_blank" rel="noopener">{{ vm.localPackage.license }}</a>
|
||||
</div>
|
||||
|
||||
<div class="umb-info-local-item">
|
||||
<strong><localize key="packager_packageReadme">Read me</localize></strong><br>
|
||||
<small ng-bind-html="vm.localPackage.readme"></small>
|
||||
</div>
|
||||
|
||||
<div class="umb-info-local-item mt4 flex items-center flex-column" ng-if="vm.installState.status == '' && vm.localPackage.isCompatible">
|
||||
|
||||
<umb-checkbox model="vm.localPackage.packageLicenseAccept" required="true">
|
||||
<strong class="label-text"><localize key="packager_accept">I accept</localize> <a href="#" ng-href="{{vm.localPackage.licenseUrl}}" target="_blank" rel="noopener"><localize key="packager_termsOfUse">terms of use</localize></a></strong>
|
||||
</umb-checkbox>
|
||||
|
||||
<umb-button class="mt3"
|
||||
type="button"
|
||||
size="m"
|
||||
button-style="success"
|
||||
disabled="localPackageForm.$invalid"
|
||||
action="vm.installPackage()"
|
||||
label-key="packager_packageInstall">
|
||||
</umb-button>
|
||||
</div>
|
||||
|
||||
<div class="umb-info-local-item">
|
||||
<umb-progress-bar ng-if="vm.installState.status !== ''"
|
||||
percentage="{{vm.installState.progress}}">
|
||||
</umb-progress-bar>
|
||||
</div>
|
||||
|
||||
<div class="umb-info-local-item text-error" ng-if="!vm.localPackage.isCompatible">
|
||||
<localize key="packager_targetVersionMismatch">This package cannot be installed, it requires a minimum Umbraco version of</localize> {{vm.localPackage.umbracoVersion}}
|
||||
</div>
|
||||
<div class="umb-info-local-item text-info">
|
||||
<p>{{vm.installState.status}}</p>
|
||||
</div>
|
||||
|
||||
<div class="umb-info-local-item text-info"
|
||||
ng-show="vm.installCompleted">
|
||||
|
||||
<button type="button"
|
||||
class="btn btn-success flex-inline mt3"
|
||||
ng-click="vm.reloadPage()">
|
||||
<localize key="packager_installFinish">Finish</localize>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -17,15 +17,12 @@
|
||||
<ProjectReference Include="../Umbraco.Web.Website/Umbraco.Web.Website.csproj" />
|
||||
<ProjectReference Include="../Umbraco.Persistence.SqlCe/Umbraco.Persistence.SqlCe.csproj" Condition="'$(OS)' == 'Windows_NT'" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.ICU.ICU4C.Runtime" Version="68.2.0.9" />
|
||||
|
||||
<RuntimeHostConfigurationOption
|
||||
Condition="$(RuntimeIdentifier.StartsWith('linux')) Or $(RuntimeIdentifier.StartsWith('win')) Or ('$(RuntimeIdentifier)' == '' And !$([MSBuild]::IsOSPlatform('osx')))"
|
||||
Include="System.Globalization.AppLocalIcu"
|
||||
Value="68.2.0.9" />
|
||||
<RuntimeHostConfigurationOption Condition="$(RuntimeIdentifier.StartsWith('linux')) Or $(RuntimeIdentifier.StartsWith('win')) Or ('$(RuntimeIdentifier)' == '' And !$([MSBuild]::IsOSPlatform('osx')))" Include="System.Globalization.AppLocalIcu" Value="68.2.0.9" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -39,6 +36,7 @@
|
||||
<Compile Remove="umbraco/mediacache/**" />
|
||||
<Compile Remove="wwwroot/umbraco/**" />
|
||||
<Compile Remove="App_Data/**" />
|
||||
<Compile Remove="Controllers\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -46,6 +44,7 @@
|
||||
<EmbeddedResource Remove="umbraco/Data/**" />
|
||||
<EmbeddedResource Remove="umbraco/logs/**" />
|
||||
<EmbeddedResource Remove="umbraco/mediacache/**" />
|
||||
<EmbeddedResource Remove="Controllers\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -55,6 +54,7 @@
|
||||
<Content Remove="umbraco/mediacache/**" />
|
||||
<Content Remove="umbraco\UmbracoWebsite\NotFound.cshtml" />
|
||||
<Content Remove="wwwroot/Web.config" />
|
||||
<Content Remove="Controllers\**" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
<None Remove="umbraco/logs/**" />
|
||||
<None Remove="umbraco/mediacache/**" />
|
||||
<None Include="umbraco/UmbracoWebsite/NoNodes.cshtml" />
|
||||
<None Remove="Controllers\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- We don't want to include the generated files, they will throw a lot of errors -->
|
||||
|
||||
Reference in New Issue
Block a user