working on: U4-6756 Don't ship or generate unnecessary folders
This commit is contained in:
@@ -97,15 +97,8 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<SystemFolders Include="$(WebAppFolder)App_Plugins" />
|
||||
<SystemFolders Include="$(WebAppFolder)App_Code" />
|
||||
<SystemFolders Include="$(WebAppFolder)App_Data" />
|
||||
<SystemFolders Include="$(WebAppFolder)Media" />
|
||||
<SystemFolders Include="$(WebAppFolder)Masterpages" />
|
||||
<SystemFolders Include="$(WebAppFolder)Scripts" />
|
||||
<SystemFolders Include="$(WebAppFolder)Css" />
|
||||
<SystemFolders Include="$(WebAppFolder)Xslt" />
|
||||
<SystemFolders Include="$(WebAppFolder)UserControls" />
|
||||
<SystemFolders Include="$(WebAppFolder)Views" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
<file src="..\_BuildOutput\WebApp\Global.asax" target="Content\Global.asax" />
|
||||
<file src="..\_BuildOutput\WebApp\Web.config" target="UmbracoFiles\Web.config" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Browsers\**" target="UmbracoFiles\App_Browsers" />
|
||||
<file src="..\_BuildOutput\WebApp\App_Plugins\**" target="UmbracoFiles\App_Plugins" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\amd64\**" target="UmbracoFiles\bin\amd64" />
|
||||
<file src="..\_BuildOutput\WebApp\bin\x86\**" target="UmbracoFiles\bin\x86" />
|
||||
<file src="..\_BuildOutput\WebApp\config\splashes\**" target="UmbracoFiles\Config\splashes" />
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- Blocks public downloading of anything in this folder and sub folders -->
|
||||
|
||||
<configuration>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<validation validateIntegratedModeConfiguration="false" />
|
||||
<handlers>
|
||||
<remove name="BlockViewHandler"/>
|
||||
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- Blocks public downloading of anything in this folder and sub folders -->
|
||||
|
||||
<configuration>
|
||||
<system.web>
|
||||
<httpHandlers>
|
||||
<add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
||||
</httpHandlers>
|
||||
</system.web>
|
||||
<system.webServer>
|
||||
<validation validateIntegratedModeConfiguration="false" />
|
||||
<handlers>
|
||||
<remove name="BlockViewHandler"/>
|
||||
<add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.HttpNotFoundHandler" />
|
||||
</handlers>
|
||||
</system.webServer>
|
||||
</configuration>
|
||||
86
src/Umbraco.Core/FileResources/Files.Designer.cs
generated
Normal file
86
src/Umbraco.Core/FileResources/Files.Designer.cs
generated
Normal file
@@ -0,0 +1,86 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <auto-generated>
|
||||
// This code was generated by a tool.
|
||||
// Runtime Version:4.0.30319.0
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
// </auto-generated>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace Umbraco.Core.FileResources {
|
||||
using System;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||
/// </summary>
|
||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||
// class via a tool like ResGen or Visual Studio.
|
||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||
// with the /str option, or rebuild your VS project.
|
||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||
internal class Files {
|
||||
|
||||
private static global::System.Resources.ResourceManager resourceMan;
|
||||
|
||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||
|
||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||
internal Files() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the cached ResourceManager instance used by this class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||
get {
|
||||
if (object.ReferenceEquals(resourceMan, null)) {
|
||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Umbraco.Core.FileResources.Files", typeof(Files).Assembly);
|
||||
resourceMan = temp;
|
||||
}
|
||||
return resourceMan;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the current thread's CurrentUICulture property for all
|
||||
/// resource lookups using this strongly typed resource class.
|
||||
/// </summary>
|
||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||
internal static global::System.Globalization.CultureInfo Culture {
|
||||
get {
|
||||
return resourceCulture;
|
||||
}
|
||||
set {
|
||||
resourceCulture = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Looks up a localized string similar to <?xml version="1.0"?>
|
||||
///
|
||||
///<!-- Blocks public downloading of anything in this folder and sub folders -->
|
||||
///
|
||||
///<configuration>
|
||||
/// <system.web>
|
||||
/// <httpHandlers>
|
||||
/// <add path="*" verb="*" type="System.Web.HttpNotFoundHandler"/>
|
||||
/// </httpHandlers>
|
||||
/// </system.web>
|
||||
/// <system.webServer>
|
||||
/// <validation validateIntegratedModeConfiguration="false" />
|
||||
/// <handlers>
|
||||
/// <remove name="BlockViewHandler"/>
|
||||
/// <add name="BlockViewHandler" path="*" verb="*" preCondition="integratedMode" type="System.Web.H [rest of string was truncated]";.
|
||||
/// </summary>
|
||||
internal static string BlockingWebConfig {
|
||||
get {
|
||||
return ResourceManager.GetString("BlockingWebConfig", resourceCulture);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
124
src/Umbraco.Core/FileResources/Files.resx
Normal file
124
src/Umbraco.Core/FileResources/Files.resx
Normal file
@@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
|
||||
<data name="BlockingWebConfig" type="System.Resources.ResXFileRef, System.Windows.Forms">
|
||||
<value>blockingweb.config;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;Windows-1252</value>
|
||||
</data>
|
||||
</root>
|
||||
@@ -6,6 +6,7 @@ using System.IO;
|
||||
using System.Configuration;
|
||||
using System.Web;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Web.Hosting;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.Logging;
|
||||
|
||||
@@ -67,7 +68,7 @@ namespace Umbraco.Core.IO
|
||||
if (tag.Groups[1].Success)
|
||||
url = tag.Groups[1].Value;
|
||||
|
||||
if (string.IsNullOrEmpty(url) == false)
|
||||
if (String.IsNullOrEmpty(url) == false)
|
||||
{
|
||||
string resolvedUrl = (url.Substring(0, 1) == "/") ? ResolveUrl(url.Substring(1)) : ResolveUrl(url);
|
||||
text = text.Replace(url, resolvedUrl);
|
||||
@@ -92,10 +93,10 @@ namespace Umbraco.Core.IO
|
||||
if (useHttpContext && HttpContext.Current != null)
|
||||
{
|
||||
//string retval;
|
||||
if (string.IsNullOrEmpty(path) == false && (path.StartsWith("~") || path.StartsWith(SystemDirectories.Root)))
|
||||
return System.Web.Hosting.HostingEnvironment.MapPath(path);
|
||||
if (String.IsNullOrEmpty(path) == false && (path.StartsWith("~") || path.StartsWith(SystemDirectories.Root)))
|
||||
return HostingEnvironment.MapPath(path);
|
||||
else
|
||||
return System.Web.Hosting.HostingEnvironment.MapPath("~/" + path.TrimStart('/'));
|
||||
return HostingEnvironment.MapPath("~/" + path.TrimStart('/'));
|
||||
}
|
||||
|
||||
var root = GetRootDirectorySafe();
|
||||
@@ -115,7 +116,7 @@ namespace Umbraco.Core.IO
|
||||
{
|
||||
string retval = ConfigurationManager.AppSettings[settingsKey];
|
||||
|
||||
if (string.IsNullOrEmpty(retval))
|
||||
if (String.IsNullOrEmpty(retval))
|
||||
retval = standardPath;
|
||||
|
||||
return retval.TrimEnd('/');
|
||||
@@ -232,7 +233,7 @@ namespace Umbraco.Core.IO
|
||||
/// <returns></returns>
|
||||
internal static string GetRootDirectorySafe()
|
||||
{
|
||||
if (string.IsNullOrEmpty(_rootDir) == false)
|
||||
if (String.IsNullOrEmpty(_rootDir) == false)
|
||||
{
|
||||
return _rootDir;
|
||||
}
|
||||
@@ -241,7 +242,7 @@ namespace Umbraco.Core.IO
|
||||
var uri = new Uri(codeBase);
|
||||
var path = uri.LocalPath;
|
||||
var baseDirectory = Path.GetDirectoryName(path);
|
||||
if (string.IsNullOrEmpty(baseDirectory))
|
||||
if (String.IsNullOrEmpty(baseDirectory))
|
||||
throw new Exception("No root directory could be resolved. Please ensure that your Umbraco solution is correctly configured.");
|
||||
|
||||
_rootDir = baseDirectory.Contains("bin")
|
||||
@@ -253,8 +254,8 @@ namespace Umbraco.Core.IO
|
||||
|
||||
internal static string GetRootDirectoryBinFolder()
|
||||
{
|
||||
string binFolder = string.Empty;
|
||||
if (string.IsNullOrEmpty(_rootDir))
|
||||
string binFolder = String.Empty;
|
||||
if (String.IsNullOrEmpty(_rootDir))
|
||||
{
|
||||
binFolder = Assembly.GetExecutingAssembly().GetAssemblyFile().Directory.FullName;
|
||||
return binFolder;
|
||||
@@ -298,5 +299,25 @@ namespace Umbraco.Core.IO
|
||||
// use string extensions
|
||||
return filePath.ToSafeFileName();
|
||||
}
|
||||
|
||||
public static void EnsurePathExists(string path)
|
||||
{
|
||||
var absolutePath = IOHelper.MapPath(path);
|
||||
if (Directory.Exists(absolutePath) == false)
|
||||
Directory.CreateDirectory(absolutePath);
|
||||
}
|
||||
|
||||
public static void EnsureFileExists(string path, string contents)
|
||||
{
|
||||
var absolutePath = IOHelper.MapPath(path);
|
||||
if (File.Exists(absolutePath) == false)
|
||||
{
|
||||
using (var writer = File.CreateText(absolutePath))
|
||||
{
|
||||
writer.Write(contents);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,11 +96,15 @@ namespace Umbraco.Core.Manifest
|
||||
|
||||
if (depth < 1)
|
||||
{
|
||||
var dirs = currDir.GetDirectories();
|
||||
var result = new List<string>();
|
||||
foreach (var d in dirs)
|
||||
if (currDir.Exists)
|
||||
{
|
||||
result.AddRange(GetAllManifestFileContents(d));
|
||||
var dirs = currDir.GetDirectories();
|
||||
|
||||
foreach (var d in dirs)
|
||||
{
|
||||
result.AddRange(GetAllManifestFileContents(d));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -328,6 +328,11 @@
|
||||
<Compile Include="Events\SendToPublishEventArgs.cs" />
|
||||
<Compile Include="Exceptions\InvalidCompositionException.cs" />
|
||||
<Compile Include="Exceptions\UmbracoStartupFailedException.cs" />
|
||||
<Compile Include="FileResources\Files.Designer.cs">
|
||||
<AutoGen>True</AutoGen>
|
||||
<DesignTime>True</DesignTime>
|
||||
<DependentUpon>Files.resx</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="HideFromTypeFinderAttribute.cs" />
|
||||
<Compile Include="HttpContextExtensions.cs" />
|
||||
<Compile Include="IApplicationEventHandler.cs" />
|
||||
@@ -1347,6 +1352,7 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="FileResources\BlockingWeb.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
@@ -1358,7 +1364,12 @@
|
||||
<ItemGroup>
|
||||
<Content Include="Strings\Notes.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="FileResources\Files.resx">
|
||||
<Generator>ResXFileCodeGenerator</Generator>
|
||||
<LastGenOutput>Files.Designer.cs</LastGenOutput>
|
||||
</EmbeddedResource>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\nuget.targets" />
|
||||
<Import Project="..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets" Condition="Exists('..\packages\Microsoft.Bcl.Build.1.0.14\tools\Microsoft.Bcl.Build.targets')" />
|
||||
|
||||
272
src/Umbraco.Web.UI/App_Start/Test.cs
Normal file
272
src/Umbraco.Web.UI/App_Start/Test.cs
Normal file
@@ -0,0 +1,272 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Web;
|
||||
using Microsoft.Owin;
|
||||
using Microsoft.Owin.Security.Google;
|
||||
using Microsoft.Owin.Security.OpenIdConnect;
|
||||
using Owin;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.Security;
|
||||
using Umbraco.Web.Security.Identity;
|
||||
using Umbraco.Web.UI.App_Start;
|
||||
|
||||
[assembly: OwinStartup("UmbracoStandardOwinStartup2", typeof(UmbracoStandardOwinStartup2))]
|
||||
|
||||
namespace Umbraco.Web.UI.App_Start
|
||||
{
|
||||
/// <summary>
|
||||
/// A custom way to configure OWIN for Umbraco
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The startup type is specified in appSettings under owin:appStartup - change it to "CustomUmbracoStartup" to use this class
|
||||
///
|
||||
/// This startup class would allow you to customize the Identity IUserStore and/or IUserManager for the Umbraco Backoffice
|
||||
/// </remarks>
|
||||
public class UmbracoCustomOwinStartup
|
||||
{
|
||||
public void Configuration(IAppBuilder app)
|
||||
{
|
||||
//Configure the Identity user manager and user store for use with Umbraco Back office
|
||||
|
||||
// *** EXPERT: overloads of this method allow you to specify a custom UserStore or even a custom UserManager!
|
||||
// *** If you plan to implement your own custom 2 factor auth, you will need a custom implementation of:
|
||||
// *** BackOfficeUserManager & BackOfficeUserStore. Your custom BackOfficeUserManager will need to override/implement:
|
||||
// *** - SupportsUserTwoFactor to return true
|
||||
// *** - Umbraco.Web.Security.Identity.IUmbracoBackOfficeTwoFactorOptions
|
||||
// *** The result view returned from IUmbracoBackOfficeTwoFactorOptions.GetTwoFactorView will be the angular
|
||||
// *** view displayed to the user to enter the 2 factor authentication code. You will need to create/implement
|
||||
// *** the custom angular view and all logic to handle the REST call to verify the code and log the user in
|
||||
// *** based on the username provided on the $scope of your view.
|
||||
// *** Your custom BackOfficeUserStore will need to override/implement:
|
||||
// *** - Microsoft.AspNet.Identity.IUserTwoFactorStore<BackOfficeIdentityUser, int>
|
||||
|
||||
app.ConfigureUserManagerForUmbracoBackOffice(
|
||||
ApplicationContext.Current,
|
||||
global::Umbraco.Core.Security.MembershipProviderExtensions.GetUsersMembershipProvider().AsUmbracoMembershipProvider());
|
||||
|
||||
//Ensure owin is configured for Umbraco back office authentication
|
||||
app
|
||||
.UseUmbracoBackOfficeCookieAuthentication(ApplicationContext.Current)
|
||||
.UseUmbracoBackOfficeExternalCookieAuthentication(ApplicationContext.Current);
|
||||
/*
|
||||
* Configure external logins for the back office:
|
||||
*
|
||||
* Depending on the authentication sources you would like to enable, you will need to install
|
||||
* certain Nuget packages.
|
||||
*
|
||||
* For Google auth: Install-Package UmbracoCms.IdentityExtensions.Google
|
||||
* For Facebook auth: Install-Package UmbracoCms.IdentityExtensions.Facebook
|
||||
* For Microsoft auth: Install-Package UmbracoCms.IdentityExtensions.Microsoft
|
||||
* For Azure ActiveDirectory auth: Install-Package UmbracoCms.IdentityExtensions.AzureActiveDirectory
|
||||
*
|
||||
* There are many more providers such as Twitter, Yahoo, ActiveDirectory, etc... most information can
|
||||
* be found here: http://www.asp.net/web-api/overview/security/external-authentication-services
|
||||
*
|
||||
* For sample code on using external providers with the Umbraco back office, install one of the
|
||||
* packages listed above to review it's code samples
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* To configure a simple auth token server for the back office:
|
||||
*
|
||||
* By default the CORS policy is to allow all requests
|
||||
*
|
||||
* app.UseUmbracoBackOfficeTokenAuth(new BackOfficeAuthServerProviderOptions());
|
||||
*
|
||||
* If you want to have a custom CORS policy for the token server you can provide
|
||||
* a custom CORS policy, example:
|
||||
*
|
||||
* app.UseUmbracoBackOfficeTokenAuth(
|
||||
* new BackOfficeAuthServerProviderOptions()
|
||||
* {
|
||||
* //Modify the CorsPolicy as required
|
||||
* CorsPolicy = new CorsPolicy()
|
||||
* {
|
||||
* AllowAnyHeader = true,
|
||||
* AllowAnyMethod = true,
|
||||
* Origins = { "http://mywebsite.com" }
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The standard way to configure OWIN for Umbraco
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The startup type is specified in appSettings under owin:appStartup - change it to "StandardUmbracoStartup" to use this class
|
||||
/// </remarks>
|
||||
public class UmbracoStandardOwinStartup2 : UmbracoDefaultOwinStartup
|
||||
{
|
||||
public override void Configuration(IAppBuilder app)
|
||||
{
|
||||
//ensure the default options are configured
|
||||
base.Configuration(app);
|
||||
|
||||
//
|
||||
app.ConfigureBackOfficeGoogleAuth(
|
||||
"1072120697051-p41pro11srud3o3n90j7m00geq426jqt.apps.googleusercontent.com",
|
||||
"cs_LJTXh2rtI01C5OIt9WFkt");
|
||||
|
||||
/*
|
||||
* Configure external logins for the back office:
|
||||
*
|
||||
* Depending on the authentication sources you would like to enable, you will need to install
|
||||
* certain Nuget packages.
|
||||
*
|
||||
* For Google auth: Install-Package UmbracoCms.IdentityExtensions.Google
|
||||
* For Facebook auth: Install-Package UmbracoCms.IdentityExtensions.Facebook
|
||||
* For Microsoft auth: Install-Package UmbracoCms.IdentityExtensions.Microsoft
|
||||
* For Azure ActiveDirectory auth: Install-Package UmbracoCms.IdentityExtensions.AzureActiveDirectory
|
||||
*
|
||||
* There are many more providers such as Twitter, Yahoo, ActiveDirectory, etc... most information can
|
||||
* be found here: http://www.asp.net/web-api/overview/security/external-authentication-services
|
||||
*
|
||||
* For sample code on using external providers with the Umbraco back office, install one of the
|
||||
* packages listed above to review it's code samples
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* To configure a simple auth token server for the back office:
|
||||
*
|
||||
* By default the CORS policy is to allow all requests
|
||||
*
|
||||
* app.UseUmbracoBackOfficeTokenAuth(new BackOfficeAuthServerProviderOptions());
|
||||
*
|
||||
* If you want to have a custom CORS policy for the token server you can provide
|
||||
* a custom CORS policy, example:
|
||||
*
|
||||
* app.UseUmbracoBackOfficeTokenAuth(
|
||||
* new BackOfficeAuthServerProviderOptions()
|
||||
* {
|
||||
* //Modify the CorsPolicy as required
|
||||
* CorsPolicy = new CorsPolicy()
|
||||
* {
|
||||
* AllowAnyHeader = true,
|
||||
* AllowAnyMethod = true,
|
||||
* Origins = { "http://mywebsite.com" }
|
||||
* }
|
||||
* });
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static class UmbracoGoogleAuthExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Configure google sign-in
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="clientId"></param>
|
||||
/// <param name="clientSecret"></param>
|
||||
/// <param name="caption"></param>
|
||||
/// <param name="style"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <remarks>
|
||||
///
|
||||
/// Nuget installation:
|
||||
/// Microsoft.Owin.Security.Google
|
||||
///
|
||||
/// Google account documentation for ASP.Net Identity can be found:
|
||||
///
|
||||
/// http://www.asp.net/web-api/overview/security/external-authentication-services#GOOGLE
|
||||
///
|
||||
/// Google apps can be created here:
|
||||
///
|
||||
/// https://developers.google.com/accounts/docs/OpenIDConnect#getcredentials
|
||||
///
|
||||
/// </remarks>
|
||||
public static void ConfigureBackOfficeGoogleAuth(this IAppBuilder app, string clientId, string clientSecret,
|
||||
string caption = "Google", string style = "btn-google-plus", string icon = "fa-google-plus")
|
||||
{
|
||||
var googleOptions = new GoogleOAuth2AuthenticationOptions()
|
||||
{
|
||||
ClientId = clientId,
|
||||
ClientSecret = clientSecret,
|
||||
//In order to allow using different google providers on the front-end vs the back office,
|
||||
// these settings are very important to make them distinguished from one another.
|
||||
SignInAsAuthenticationType = global::Umbraco.Core.Constants.Security.BackOfficeExternalAuthenticationType,
|
||||
// By default this is '/signin-google', you will need to change that default value in your
|
||||
// Google developer settings for your web-app in the "REDIRECT URIS" setting
|
||||
CallbackPath = new PathString("/umbraco-google-signin")
|
||||
};
|
||||
|
||||
googleOptions.SetExternalSignInAutoLinkOptions(
|
||||
new ExternalSignInAutoLinkOptions(
|
||||
autoLinkExternalAccount: true));
|
||||
|
||||
googleOptions.ForUmbracoBackOffice(style, icon);
|
||||
googleOptions.Caption = caption;
|
||||
app.UseGoogleAuthentication(googleOptions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configure ActiveDirectory sign-in
|
||||
/// </summary>
|
||||
/// <param name="app"></param>
|
||||
/// <param name="tenant"></param>
|
||||
/// <param name="clientId"></param>
|
||||
/// <param name="postLoginRedirectUri">
|
||||
/// The URL that will be redirected to after login is successful, example: http://mydomain.com/umbraco/;
|
||||
/// </param>
|
||||
/// <param name="issuerId">
|
||||
///
|
||||
/// This is the "Issuer Id" for you Azure AD application. This a GUID value and can be found
|
||||
/// in the Azure portal when viewing your configured application and clicking on 'View endpoints'
|
||||
/// which will list all of the API endpoints. Each endpoint will contain a GUID value, this is
|
||||
/// the Issuer Id which must be used for this value.
|
||||
///
|
||||
/// If this value is not set correctly then accounts won't be able to be detected
|
||||
/// for un-linking in the back office.
|
||||
///
|
||||
/// </param>
|
||||
/// <param name="caption"></param>
|
||||
/// <param name="style"></param>
|
||||
/// <param name="icon"></param>
|
||||
/// <remarks>
|
||||
/// ActiveDirectory account documentation for ASP.Net Identity can be found:
|
||||
/// https://github.com/AzureADSamples/WebApp-WebAPI-OpenIDConnect-DotNet
|
||||
/// </remarks>
|
||||
public static void ConfigureBackOfficeAzureActiveDirectoryAuth(this IAppBuilder app,
|
||||
string tenant, string clientId, string postLoginRedirectUri, Guid issuerId,
|
||||
string caption = "Active Directory", string style = "btn-microsoft", string icon = "fa-windows")
|
||||
{
|
||||
var authority = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"https://login.windows.net/{0}",
|
||||
tenant);
|
||||
|
||||
var adOptions = new OpenIdConnectAuthenticationOptions
|
||||
{
|
||||
SignInAsAuthenticationType = global::Umbraco.Core.Constants.Security.BackOfficeExternalAuthenticationType,
|
||||
ClientId = clientId,
|
||||
Authority = authority,
|
||||
Notifications = new OpenIdConnectAuthenticationNotifications
|
||||
{
|
||||
RedirectToIdentityProvider = notification =>
|
||||
{
|
||||
return Task.FromResult(0);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
adOptions.SetExternalSignInAutoLinkOptions(new ExternalSignInAutoLinkOptions());
|
||||
|
||||
adOptions.ForUmbracoBackOffice(style, icon);
|
||||
adOptions.Caption = caption;
|
||||
//Need to set the auth tyep as the issuer path
|
||||
adOptions.AuthenticationType = string.Format(
|
||||
CultureInfo.InvariantCulture,
|
||||
"https://sts.windows.net/{0}/",
|
||||
issuerId);
|
||||
app.UseOpenIdConnectAuthentication(adOptions);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -2386,7 +2386,6 @@
|
||||
<Content Include="Config\log4net.config" />
|
||||
<Content Include="Config\FileSystemProviders.config" />
|
||||
<Content Include="Config\EmbeddedMedia.config" />
|
||||
<Content Include="Xslt\Web.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="App_Code\" />
|
||||
|
||||
@@ -68,21 +68,23 @@ namespace Umbraco.Web.UI.Umbraco.Developer.Macros
|
||||
//get all the partials in the normal /MacroPartials folder
|
||||
var foundMacroPartials = GetPartialViewFiles(partialsDir, partialsDir, SystemDirectories.MvcViews + "/MacroPartials");
|
||||
//now try to find all of them int he App_Plugins/[PackageName]/Views/MacroPartials folder
|
||||
var partialPluginsDir = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins));
|
||||
foreach(var d in partialPluginsDir.GetDirectories())
|
||||
{
|
||||
var viewsFolder = d.GetDirectories("Views");
|
||||
if (viewsFolder.Any())
|
||||
{
|
||||
var macroPartials = viewsFolder.First().GetDirectories("MacroPartials");
|
||||
if (macroPartials.Any())
|
||||
{
|
||||
foundMacroPartials = foundMacroPartials.Concat(
|
||||
GetPartialViewFiles(macroPartials.First().FullName, macroPartials.First().FullName, SystemDirectories.AppPlugins + "/" + d.Name + "/Views/MacroPartials"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var appPluginsFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins));
|
||||
if (appPluginsFolder.Exists)
|
||||
{
|
||||
foreach (var d in appPluginsFolder.GetDirectories())
|
||||
{
|
||||
var viewsFolder = d.GetDirectories("Views");
|
||||
if (viewsFolder.Any())
|
||||
{
|
||||
var macroPartials = viewsFolder.First().GetDirectories("MacroPartials");
|
||||
if (macroPartials.Any())
|
||||
{
|
||||
foundMacroPartials = foundMacroPartials.Concat(
|
||||
GetPartialViewFiles(macroPartials.First().FullName, macroPartials.First().FullName, SystemDirectories.AppPlugins + "/" + d.Name + "/Views/MacroPartials"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PartialViewList.DataSource = foundMacroPartials;
|
||||
|
||||
@@ -42,8 +42,10 @@ namespace Umbraco.Web.Install
|
||||
{
|
||||
errorReport = new List<string>();
|
||||
bool succes = true;
|
||||
foreach (string dir in PermissionDirs)
|
||||
foreach (string dir in directories)
|
||||
{
|
||||
if (Directory.Exists(dir) == false) continue;
|
||||
|
||||
bool result = SaveAndDeleteFile(IOHelper.MapPath(dir + "/configWizardPermissionTest.txt"));
|
||||
|
||||
if (result == false)
|
||||
|
||||
@@ -16,25 +16,11 @@ namespace umbraco.presentation
|
||||
{
|
||||
base.ApplicationInitialized(umbracoApplication, applicationContext);
|
||||
|
||||
EnsurePathExists("~/App_Code");
|
||||
EnsurePathExists("~/App_Data");
|
||||
EnsurePathExists(SystemDirectories.AppPlugins);
|
||||
EnsurePathExists(SystemDirectories.Css);
|
||||
EnsurePathExists(SystemDirectories.Masterpages);
|
||||
EnsurePathExists(SystemDirectories.Media);
|
||||
EnsurePathExists(SystemDirectories.Scripts);
|
||||
EnsurePathExists(SystemDirectories.UserControls);
|
||||
EnsurePathExists(SystemDirectories.Xslt);
|
||||
EnsurePathExists(SystemDirectories.MvcViews);
|
||||
EnsurePathExists(SystemDirectories.MvcViews + "/Partials");
|
||||
EnsurePathExists(SystemDirectories.MvcViews + "/MacroPartials");
|
||||
}
|
||||
|
||||
public void EnsurePathExists(string path)
|
||||
{
|
||||
var absolutePath = IOHelper.MapPath(path);
|
||||
if (!Directory.Exists(absolutePath))
|
||||
Directory.CreateDirectory(absolutePath);
|
||||
IOHelper.EnsurePathExists("~/App_Data");
|
||||
IOHelper.EnsurePathExists(SystemDirectories.Media);
|
||||
IOHelper.EnsurePathExists(SystemDirectories.MvcViews);
|
||||
IOHelper.EnsurePathExists(SystemDirectories.MvcViews + "/Partials");
|
||||
IOHelper.EnsurePathExists(SystemDirectories.MvcViews + "/MacroPartials");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -87,7 +87,7 @@ namespace umbraco
|
||||
private void RenderTemplateFolderItems(string folder, string folderPath, ref XmlTree tree)
|
||||
{
|
||||
string relPath = SystemDirectories.Masterpages + "/" + folder;
|
||||
if (!string.IsNullOrEmpty(folderPath))
|
||||
if (string.IsNullOrEmpty(folderPath) == false)
|
||||
relPath += folderPath;
|
||||
|
||||
string fullPath = IOHelper.MapPath(relPath);
|
||||
@@ -160,26 +160,31 @@ namespace umbraco
|
||||
{
|
||||
if (base.m_id == -1)
|
||||
{
|
||||
foreach (string s in Directory.GetDirectories(IOHelper.MapPath(SystemDirectories.Masterpages)))
|
||||
var dir = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.Masterpages));
|
||||
if (dir.Exists)
|
||||
{
|
||||
var _s = Path.GetFileNameWithoutExtension(s);
|
||||
|
||||
XmlTreeNode xNode = XmlTreeNode.Create(this);
|
||||
xNode.NodeID = _s;
|
||||
xNode.Text = _s;
|
||||
xNode.Icon = "icon-folder";
|
||||
xNode.OpenIcon = "icon-folder";
|
||||
xNode.Source = GetTreeServiceUrl(_s) + "&folder=" + _s;
|
||||
xNode.HasChildren = true;
|
||||
xNode.Menu.Clear();
|
||||
xNode.Menu.Add(ActionRefresh.Instance);
|
||||
|
||||
OnBeforeNodeRender(ref tree, ref xNode, EventArgs.Empty);
|
||||
if (xNode != null)
|
||||
foreach (var s in dir.GetDirectories())
|
||||
{
|
||||
tree.Add(xNode);
|
||||
OnAfterNodeRender(ref tree, ref xNode, EventArgs.Empty);
|
||||
var _s = Path.GetFileNameWithoutExtension(s.FullName);
|
||||
|
||||
XmlTreeNode xNode = XmlTreeNode.Create(this);
|
||||
xNode.NodeID = _s;
|
||||
xNode.Text = _s;
|
||||
xNode.Icon = "icon-folder";
|
||||
xNode.OpenIcon = "icon-folder";
|
||||
xNode.Source = GetTreeServiceUrl(_s) + "&folder=" + _s;
|
||||
xNode.HasChildren = true;
|
||||
xNode.Menu.Clear();
|
||||
xNode.Menu.Add(ActionRefresh.Instance);
|
||||
|
||||
OnBeforeNodeRender(ref tree, ref xNode, EventArgs.Empty);
|
||||
if (xNode != null)
|
||||
{
|
||||
tree.Add(xNode);
|
||||
OnAfterNodeRender(ref tree, ref xNode, EventArgs.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Data;
|
||||
using System.IO;
|
||||
using System.Web.Security;
|
||||
using Umbraco.Core;
|
||||
using Umbraco.Core.IO;
|
||||
@@ -9,6 +10,7 @@ using umbraco.BusinessLogic;
|
||||
using umbraco.DataLayer;
|
||||
using umbraco.BasePages;
|
||||
using umbraco.cms.businesslogic.member;
|
||||
using Umbraco.Core.FileResources;
|
||||
|
||||
namespace umbraco
|
||||
{
|
||||
@@ -22,14 +24,17 @@ namespace umbraco
|
||||
|
||||
public override bool PerformSave()
|
||||
{
|
||||
IOHelper.EnsurePathExists(SystemDirectories.Xslt);
|
||||
IOHelper.EnsureFileExists(Path.Combine(IOHelper.MapPath(SystemDirectories.Xslt), "web.config"), Files.BlockingWebConfig);
|
||||
|
||||
var template = Alias.Substring(0, Alias.IndexOf("|||"));
|
||||
var fileName = Alias.Substring(Alias.IndexOf("|||") + 3, Alias.Length - Alias.IndexOf("|||") - 3).Replace(" ", "");
|
||||
if (!fileName.ToLowerInvariant().EndsWith(".xslt"))
|
||||
if (fileName.ToLowerInvariant().EndsWith(".xslt") == false)
|
||||
fileName += ".xslt";
|
||||
var xsltTemplateSource = IOHelper.MapPath(SystemDirectories.Umbraco + "/xslt/templates/" + template);
|
||||
var xsltNewFilename = IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName);
|
||||
|
||||
if (!System.IO.File.Exists(xsltNewFilename))
|
||||
if (File.Exists(xsltNewFilename) == false)
|
||||
{
|
||||
if (fileName.Contains("/")) //if there's a / create the folder structure for it
|
||||
{
|
||||
|
||||
@@ -130,6 +130,8 @@ namespace umbraco.cms.presentation.developer
|
||||
{
|
||||
var dirInfo = new DirectoryInfo(path);
|
||||
|
||||
if (dirInfo.Exists == false) return;
|
||||
|
||||
// Populate subdirectories
|
||||
var dirInfos = dirInfo.GetDirectories();
|
||||
foreach (var dir in dirInfos)
|
||||
@@ -269,6 +271,7 @@ namespace umbraco.cms.presentation.developer
|
||||
private void PopulateUserControls(string path)
|
||||
{
|
||||
var directoryInfo = new DirectoryInfo(path);
|
||||
if (directoryInfo.Exists == false) return;
|
||||
|
||||
var rootDir = IOHelper.MapPath(SystemDirectories.UserControls);
|
||||
|
||||
|
||||
@@ -62,6 +62,7 @@ namespace umbraco.presentation.webservices
|
||||
{
|
||||
if (AuthorizeRequest(DefaultApps.developer.ToString()))
|
||||
{
|
||||
IOHelper.EnsurePathExists(SystemDirectories.Xslt);
|
||||
|
||||
// validate file
|
||||
IOHelper.ValidateEditPath(IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName),
|
||||
@@ -69,8 +70,7 @@ namespace umbraco.presentation.webservices
|
||||
// validate extension
|
||||
IOHelper.ValidateFileExtension(IOHelper.MapPath(SystemDirectories.Xslt + "/" + fileName),
|
||||
new List<string>() { "xsl", "xslt" });
|
||||
|
||||
|
||||
|
||||
StreamWriter SW;
|
||||
string tempFileName = IOHelper.MapPath(SystemDirectories.Xslt + "/" + DateTime.Now.Ticks + "_temp.xslt");
|
||||
SW = File.CreateText(tempFileName);
|
||||
@@ -392,7 +392,10 @@ namespace umbraco.presentation.webservices
|
||||
if (File.Exists(saveOldPath))
|
||||
File.Delete(saveOldPath);
|
||||
}
|
||||
|
||||
|
||||
//ensure the folder exists before saving
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
|
||||
|
||||
using (var sw = File.CreateText(savePath))
|
||||
{
|
||||
sw.Write(val);
|
||||
|
||||
@@ -276,7 +276,9 @@ namespace umbraco.presentation.webservices
|
||||
}
|
||||
|
||||
private string SaveXslt(string fileName, string fileContents, bool ignoreDebugging)
|
||||
{
|
||||
{
|
||||
IOHelper.EnsurePathExists(SystemDirectories.Xslt);
|
||||
|
||||
var tempFileName = IOHelper.MapPath(SystemDirectories.Xslt + "/" + System.DateTime.Now.Ticks + "_temp.xslt");
|
||||
using (var sw = File.CreateText(tempFileName))
|
||||
{
|
||||
@@ -412,6 +414,9 @@ namespace umbraco.presentation.webservices
|
||||
//Directory check.. only allow files in script dir and below to be edited
|
||||
if (savePath.StartsWith(IOHelper.MapPath(SystemDirectories.Scripts + "/")))
|
||||
{
|
||||
//ensure the folder exists before saving
|
||||
Directory.CreateDirectory(Path.GetDirectoryName(savePath));
|
||||
|
||||
using (var sw = File.CreateText(IOHelper.MapPath(SystemDirectories.Scripts + "/" + filename)))
|
||||
{
|
||||
sw.Write(val);
|
||||
|
||||
@@ -77,6 +77,7 @@ namespace umbraco.editorControls.userControlGrapper
|
||||
private void populateUserControls(string path)
|
||||
{
|
||||
DirectoryInfo di = new DirectoryInfo(path);
|
||||
if (di.Exists == false) return;
|
||||
|
||||
foreach (FileInfo uc in di.GetFiles("*.ascx"))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user