2018-06-29 19:52:40 +02:00
using System ;
using System.Collections.Generic ;
2018-07-11 15:58:48 +10:00
using System.Globalization ;
2018-06-29 19:52:40 +02:00
using System.Linq ;
using System.Net ;
using System.Net.Http ;
using System.Text ;
2018-07-11 15:58:48 +10:00
using System.Threading ;
2018-06-29 19:52:40 +02:00
using System.Web.Http ;
using System.Web.SessionState ;
using Umbraco.Web.Models.ContentEditing ;
using Umbraco.Web.Mvc ;
using Umbraco.Core ;
using Umbraco.Core.Models ;
using Umbraco.Core.Models.PublishedContent ;
2019-01-21 10:55:48 +01:00
using Umbraco.Core.Services ;
2018-06-29 19:52:40 +02:00
namespace Umbraco.Web.Editors
{
/// <summary>
/// API controller to deal with Macro data
/// </summary>
/// <remarks>
/// Note that this implements IRequiresSessionState which will enable HttpContext.Session - generally speaking we don't normally
/// enable this for webapi controllers, however since this controller is used to render macro content and macros can access
/// Session, we don't want it to throw null reference exceptions.
/// </remarks>
[PluginController("UmbracoApi")]
2019-01-04 11:44:23 +01:00
public class MacroRenderingController : UmbracoAuthorizedJsonController , IRequiresSessionState
2018-06-29 19:52:40 +02:00
{
2019-01-21 10:55:48 +01:00
private readonly IMacroService _macroService ;
private readonly IContentService _contentService ;
2019-01-31 15:09:31 +11:00
private readonly IUmbracoComponentRenderer _componentRenderer ;
2018-06-29 19:52:40 +02:00
private readonly IVariationContextAccessor _variationContextAccessor ;
2019-01-31 15:09:31 +11:00
public MacroRenderingController ( IUmbracoComponentRenderer componentRenderer , IVariationContextAccessor variationContextAccessor , IMacroService macroService , IContentService contentService )
2018-06-29 19:52:40 +02:00
{
2019-01-31 15:09:31 +11:00
_componentRenderer = componentRenderer ;
2018-06-29 19:52:40 +02:00
_variationContextAccessor = variationContextAccessor ;
2019-01-21 10:55:48 +01:00
_macroService = macroService ;
_contentService = contentService ;
2018-06-29 19:52:40 +02:00
}
/// <summary>
/// Gets the macro parameters to be filled in for a particular macro
/// </summary>
/// <returns></returns>
/// <remarks>
2019-01-21 10:55:48 +01:00
/// Note that ALL logged in users have access to this method because editors will need to insert macros into rte (content/media/members) and it's used for
2018-06-29 19:52:40 +02:00
/// inserting into templates/views/etc... it doesn't expose any sensitive data.
/// </remarks>
public IEnumerable < MacroParameter > GetMacroParameters ( int macroId )
{
2019-01-21 10:55:48 +01:00
var macro = _macroService . GetById ( macroId ) ;
2018-06-29 19:52:40 +02:00
if ( macro = = null )
{
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
}
return Mapper . Map < IEnumerable < MacroParameter > > ( macro ) . OrderBy ( x = > x . SortOrder ) ;
}
/// <summary>
2019-01-26 10:52:19 -05:00
/// Gets a rendered macro as HTML for rendering in the rich text editor
2018-06-29 19:52:40 +02:00
/// </summary>
/// <param name="macroAlias"></param>
/// <param name="pageId"></param>
/// <param name="macroParams">
/// To send a dictionary as a GET parameter the query should be structured like:
///
/// ?macroAlias=Test&pageId=3634¯oParams[0].key=myKey¯oParams[0].value=myVal¯oParams[1].key=anotherKey¯oParams[1].value=anotherVal
///
/// </param>
/// <returns></returns>
[HttpGet]
public HttpResponseMessage GetMacroResultAsHtmlForEditor ( string macroAlias , int pageId , [ FromUri ] IDictionary < string , object > macroParams )
{
return GetMacroResultAsHtml ( macroAlias , pageId , macroParams ) ;
}
/// <summary>
2019-01-26 10:52:19 -05:00
/// Gets a rendered macro as HTML for rendering in the rich text editor.
/// Using HTTP POST instead of GET allows for more parameters to be passed as it's not dependent on URL-length limitations like GET.
2018-06-29 19:52:40 +02:00
/// The method using GET is kept to maintain backwards compatibility
/// </summary>
/// <param name="model"></param>
/// <returns></returns>
[HttpPost]
public HttpResponseMessage GetMacroResultAsHtmlForEditor ( MacroParameterModel model )
{
return GetMacroResultAsHtml ( model . MacroAlias , model . PageId , model . MacroParams ) ;
}
public class MacroParameterModel
{
public string MacroAlias { get ; set ; }
public int PageId { get ; set ; }
public IDictionary < string , object > MacroParams { get ; set ; }
}
private HttpResponseMessage GetMacroResultAsHtml ( string macroAlias , int pageId , IDictionary < string , object > macroParams )
{
2019-01-21 10:55:48 +01:00
var m = _macroService . GetByAlias ( macroAlias ) ;
2018-06-29 19:52:40 +02:00
if ( m = = null )
throw new HttpResponseException ( HttpStatusCode . NotFound ) ;
2019-02-04 13:48:53 +11:00
var publishedContent = UmbracoContext . ContentCache . GetById ( true , pageId ) ;
2018-06-29 19:52:40 +02:00
//if it isn't supposed to be rendered in the editor then return an empty string
2019-02-04 13:42:07 +11:00
//currently we cannot render a macro if the page doesn't yet exist
2019-02-04 13:48:53 +11:00
if ( pageId = = - 1 | | publishedContent = = null | | ! m . UseInEditor )
2018-06-29 19:52:40 +02:00
{
var response = Request . CreateResponse ( ) ;
2019-01-26 10:52:19 -05:00
//need to create a specific content result formatted as HTML since this controller has been configured
2018-06-29 19:52:40 +02:00
//with only json formatters.
response . Content = new StringContent ( string . Empty , Encoding . UTF8 , "text/html" ) ;
return response ;
}
2018-07-11 15:58:48 +10:00
// When rendering the macro in the backoffice the default setting would be to use the Culture of the logged in user.
// Since a Macro might contain thing thats related to the culture of the "IPublishedContent" (ie Dictionary keys) we want
// to set the current culture to the culture related to the content item. This is hacky but it works.
2019-02-11 07:54:39 +01:00
2019-04-17 10:03:49 +02:00
// fixme I don't even know how this ever worked?!
// assume this was some sort of "the culture of the item"
// but... with multilingual it does not make any sense?!
//var culture = publishedContent.GetCulture();
string culture = "" ; // needs to be eg fr-FR
2018-07-11 15:58:48 +10:00
if ( culture ! = null )
{
2019-04-17 10:03:49 +02:00
Thread . CurrentThread . CurrentCulture = Thread . CurrentThread . CurrentUICulture = CultureInfo . GetCultureInfo ( culture ) ;
_variationContextAccessor . VariationContext = new VariationContext ( culture ) ;
}
else
{
_variationContextAccessor . VariationContext = new VariationContext ( ) ; //must have an active variation context!
2018-07-11 15:58:48 +10:00
}
2018-06-29 19:52:40 +02:00
var result = Request . CreateResponse ( ) ;
2019-01-26 10:52:19 -05:00
//need to create a specific content result formatted as HTML since this controller has been configured
2018-06-29 19:52:40 +02:00
//with only json formatters.
result . Content = new StringContent (
2019-02-04 13:42:07 +11:00
_componentRenderer . RenderMacro ( pageId , m . Alias , macroParams ) . ToString ( ) ,
2018-06-29 19:52:40 +02:00
Encoding . UTF8 ,
"text/html" ) ;
return result ;
}
[HttpPost]
public HttpResponseMessage CreatePartialViewMacroWithFile ( CreatePartialViewMacroWithFileModel model )
{
if ( model = = null ) throw new ArgumentNullException ( "model" ) ;
if ( string . IsNullOrWhiteSpace ( model . Filename ) ) throw new ArgumentException ( "Filename cannot be null or whitespace" , "model.Filename" ) ;
if ( string . IsNullOrWhiteSpace ( model . VirtualPath ) ) throw new ArgumentException ( "VirtualPath cannot be null or whitespace" , "model.VirtualPath" ) ;
var macroName = model . Filename . TrimEnd ( ".cshtml" ) ;
var macro = new Macro
{
Alias = macroName . ToSafeAlias ( ) ,
Name = macroName ,
2019-02-11 07:54:39 +01:00
MacroSource = model . VirtualPath . EnsureStartsWith ( "~" ) ,
MacroType = MacroTypes . PartialView
2018-06-29 19:52:40 +02:00
} ;
2019-01-21 10:55:48 +01:00
_macroService . Save ( macro ) ; // may throw
2018-06-29 19:52:40 +02:00
return new HttpResponseMessage ( HttpStatusCode . OK ) ;
}
public class CreatePartialViewMacroWithFileModel
{
public string Filename { get ; set ; }
public string VirtualPath { get ; set ; }
}
}
}