Merge pull request #4165 from umbraco/temp8-3417-macro-crud

Macro CRUD angularized
This commit is contained in:
Bjarke Berg
2019-01-21 09:55:26 +01:00
committed by GitHub
19 changed files with 990 additions and 30 deletions

View File

@@ -125,6 +125,18 @@ namespace Umbraco.Core.Collections
}
public void ReplaceAll(IEnumerable<TValue> values)
{
if (values == null) throw new ArgumentNullException(nameof(values));
Clear();
foreach (var value in values)
{
Add(value);
}
}
public bool Remove(TKey key)
{
if (!Indecies.ContainsKey(key)) return false;

View File

@@ -20,14 +20,14 @@ function macroResource($q, $http, umbRequestHelper) {
* @param {int} macroId The macro id to get parameters for
*
*/
getMacroParameters: function (macroId) {
getMacroParameters: function(macroId) {
return umbRequestHelper.resourcePromise(
$http.get(
umbRequestHelper.getApiUrl(
"macroApiBaseUrl",
"GetMacroParameters",
[{ macroId: macroId }])),
'Failed to retrieve macro parameters for macro with id ' + macroId);
$http.get(
umbRequestHelper.getApiUrl(
"macroRenderingApiBaseUrl",
"GetMacroParameters",
[{ macroId: macroId }])),
'Failed to retrieve macro parameters for macro with id ' + macroId);
},
/**
@@ -43,13 +43,14 @@ function macroResource($q, $http, umbRequestHelper) {
* @param {Array} macroParamDictionary A dictionary of macro parameters
*
*/
getMacroResultAsHtmlForEditor: function (macroAlias, pageId, macroParamDictionary) {
getMacroResultAsHtmlForEditor: function(macroAlias, pageId, macroParamDictionary) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"macroApiBaseUrl",
"GetMacroResultAsHtmlForEditor"), {
"macroRenderingApiBaseUrl",
"GetMacroResultAsHtmlForEditor"),
{
macroAlias: macroAlias,
pageId: pageId,
macroParams: macroParamDictionary
@@ -67,17 +68,55 @@ function macroResource($q, $http, umbRequestHelper) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"macroApiBaseUrl",
"CreatePartialViewMacroWithFile"), {
virtualPath: virtualPath,
filename: filename
}
"macroRenderingApiBaseUrl",
"CreatePartialViewMacroWithFile"),
{
virtualPath: virtualPath,
filename: filename
}
),
'Failed to create macro "' + filename + '"'
);
},
createMacro: function(name) {
return umbRequestHelper.resourcePromise(
$http.post(
umbRequestHelper.getApiUrl(
"macroApiBaseUrl",
"Create?name=" + name)
),
'Failed to create macro "' + name + '"'
);
},
getPartialViews: function() {
return umbRequestHelper.resourcePromise(
$http.get(umbRequestHelper.getApiUrl("macroApiBaseUrl", "GetPartialViews"),
"Failed to get partial views")
);
},
getParameterEditors: function() {
return umbRequestHelper.resourcePromise(
$http.get(umbRequestHelper.getApiUrl("macroApiBaseUrl", "GetParameterEditors"),
"Failed to get parameter editors")
);
},
getById: function(id) {
return umbRequestHelper.resourcePromise(
$http.get(umbRequestHelper.getApiUrl("macroApiBaseUrl", "GetById", { "id": id }), "Failed to get macro")
);
},
saveMacro: function(macro) {
return umbRequestHelper.resourcePromise(
$http.post(umbRequestHelper.getApiUrl("macroApiBaseUrl", "Save"), macro)
);
}
};
};
}
angular.module('umbraco.resources').factory('macroResource', macroResource);

View File

@@ -0,0 +1,19 @@
<div ng-controller="Umbraco.Editors.Macros.CreateController as vm">
<div class="umb-pane">
<h5><localize key="create_createUnder">Create an item under</localize> {{currentNode.name}}</h5>
</div>
<div class="umb-pane">
<form name="createDictionaryForm"
ng-submit="vm.createItem()"
val-form-manager>
<umb-control-group label="Enter a item-name" hide-label="true">
<input type="text" name="itemKey" ng-model="vm.itemKey" class="umb-textstring textstring input-block-level" required />
</umb-control-group>
<button type="submit" class="btn btn-primary"><localize key="general_create">Create</localize></button>
</form>
</div>
</div>

View File

@@ -0,0 +1,30 @@
<div data-element="editor-macros" ng-controller="Umbraco.Editors.Macros.EditController as vm">
<form name="macroForm" novalidate val-form-manager ng-submit="vm.save()">
<umb-load-indicator ng-if="vm.page.loading"></umb-load-indicator>
<umb-editor-view ng-if="!vm.page.loading">
<umb-editor-header name="vm.macro.name"
alias="vm.macro.alias"
hide-description="true"
hide-icon="true"
navigation="vm.page.navigation">
</umb-editor-header>
<umb-editor-container class="form-horizontal">
<umb-editor-sub-views sub-views="vm.page.navigation" model="vm">
</umb-editor-sub-views>
</umb-editor-container>
<umb-editor-footer>
<umb-editor-footer-content-right>
<umb-button type="submit"
button-style="success"
state="vm.page.saveButtonState"
shortcut="ctrl+s"
label="Save"
label-key="buttons_save">
</umb-button>
</umb-editor-footer-content-right>
</umb-editor-footer>
</umb-editor-view>
</form>
</div>

View File

@@ -0,0 +1,27 @@
(function() {
"use strict";
function ParameterEditorController($scope, formHelper) {
const vm = this;
vm.submit = submit;
vm.close = close;
function submit() {
if ($scope.model && $scope.model.submit && formHelper.submitForm({scope: $scope})) {
$scope.model.submit($scope.model);
}
}
function close() {
if ($scope.model && $scope.model.close) {
$scope.model.close();
}
}
}
angular.module("umbraco").controller("Umbraco.Editors.ParameterEditorController", ParameterEditorController);
})();

View File

@@ -0,0 +1,58 @@
<div ng-controller="Umbraco.Editors.ParameterEditorController as vm">
<form novalidate name="parameterForm" val-form-manager>
<umb-editor-view>
<umb-editor-header name="model.title"
name-locked="true"
hide-alias="true"
hide-icon="true"
hide-description="true">
</umb-editor-header>
<umb-editor-container>
<umb-box>
<umb-box-content class="block-form">
<umb-control-group label="@general_name" description="">
<input type="text" ng-model="model.parameter.label" umb-auto-focus required />
</umb-control-group>
<umb-control-group label="key" description="">
<input type="text" ng-model="model.parameter.key" required />
</umb-control-group>
<umb-control-group label="Editor" description="">
<select required class="input-block-level" ng-model="model.parameter.editor" ng-options="i.alias as (i.name) for i in model.editors"></select>
</umb-control-group>
</umb-box-content>
</umb-box>
</umb-editor-container>
<umb-editor-footer>
<umb-editor-footer-content-right>
<umb-button type="button"
button-style="link"
label-key="general_close"
action="vm.close()">
</umb-button>
<umb-button type="button"
button-style="success"
label-key="general_submit"
state="vm.saveButtonState"
action="vm.submit(model)">
</umb-button>
</umb-editor-footer-content-right>
</umb-editor-footer>
'
</umb-editor-view>
</form>
</div>

View File

@@ -0,0 +1,44 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.Macros.CreateController
* @function
*
* @description
* The controller for creating macro items
*/
function MacrosCreateController($scope, $location, macroResource, navigationService, notificationsService, formHelper, appState) {
var vm = this;
vm.itemKey = "";
function createItem() {
var node = $scope.currentNode;
macroResource.createMacro(vm.itemKey).then(function (data) {
navigationService.hideMenu();
// set new item as active in tree
var currPath = node.path ? node.path : "-1";
navigationService.syncTree({ tree: "macros", path: currPath + "," + data, forceReload: true, activate: true });
// reset form state
formHelper.resetForm({ scope: $scope });
// navigate to edit view
var currentSection = appState.getSectionState("currentSection");
$location.path("/" + currentSection + "/macros/edit/" + data);
}, function (err) {
if (err.data && err.data.message) {
notificationsService.error(err.data.message);
navigationService.hideMenu();
}
});
}
vm.createItem = createItem;
}
angular.module("umbraco").controller("Umbraco.Editors.Macros.CreateController", MacrosCreateController);

View File

@@ -0,0 +1,155 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.Macros.EditController
* @function
*
* @description
* The controller for editing macros.
*/
function MacrosEditController($scope, $q, $routeParams, macroResource, editorState, navigationService, formHelper, contentEditingHelper, localizationService, angularHelper) {
var vm = this;
vm.promises = {};
vm.page = {};
vm.page.loading = false;
vm.page.saveButtonState = "init";
vm.page.menu = {}
function toggleValue(key) {
vm.macro[key] = !vm.macro[key];
}
vm.toggle = toggleValue;
function saveMacro() {
if (formHelper.submitForm({ scope: $scope, statusMessage: "Saving..." })) {
vm.page.saveButtonState = "busy";
macroResource.saveMacro(vm.macro).then(function (data) {
formHelper.resetForm({ scope: $scope, notifications: data.notifications });
bindMacro(data);
vm.page.saveButtonState = "success";
}, function (error) {
contentEditingHelper.handleSaveError({
redirectOnFailure: false,
err: error
});
vm.page.saveButtonState = "error";
});
}
}
vm.save = saveMacro;
function setFormDirty() {
var currentForm = angularHelper.getCurrentForm($scope);
if (currentForm) {
currentForm.$setDirty();
}
}
vm.setDirty = setFormDirty;
function getPartialViews() {
var deferred = $q.defer();
macroResource.getPartialViews().then(function (data) {
deferred.resolve(data);
}, function () {
deferred.reject();
});
return deferred.promise;
}
function getParameterEditors() {
var deferred = $q.defer();
macroResource.getParameterEditors().then(function (data) {
deferred.resolve(data);
}, function () {
deferred.reject();
});
return deferred.promise;
}
function getMacro() {
var deferred = $q.defer();
macroResource.getById($routeParams.id).then(function (data) {
deferred.resolve(data);
}, function () {
deferred.reject();
});
return deferred.promise;
}
function bindMacro(data) {
vm.macro = data;
editorState.set(vm.macro);
navigationService.syncTree({ tree: "macros", path: vm.macro.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
}
function init() {
vm.page.loading = true;
vm.promises['partialViews'] = getPartialViews();
vm.promises['parameterEditors'] = getParameterEditors();
vm.promises['macro'] = getMacro();
$q.all(vm.promises).then(function (values) {
var keys = Object.keys(values);
for (var i = 0; i < keys.length; i++) {
var key = keys[i];
if (keys[i] === 'partialViews') {
vm.views = values[key];
}
if (keys[i] === 'parameterEditors') {
vm.parameterEditors = values[key];
}
if (keys[i] === 'macro') {
bindMacro(values[key]);
}
}
vm.page.loading = false;
});
vm.page.navigation = [
{
"name": "Settings",
"alias": "settings",
"icon": "icon-settings",
"view": "views/macros/views/settings.html",
"active": true
},
{
"name": "Parameters",
"alias": "parameters",
"icon": "icon-list",
"view": "views/macros/views/parameters.html"
}
];
}
init();
}
angular.module("umbraco").controller("Umbraco.Editors.Macros.EditController", MacrosEditController);

View File

@@ -0,0 +1,78 @@
/**
* @ngdoc controller
* @name Umbraco.Editors.Macros.ParametersController
* @function
*
* @description
* The controller for editing macros parameters
*/
function MacrosParametersController($scope, editorService) {
$scope.sortableOptions = {
axis: 'y',
containment: 'parent',
cursor: 'move',
items: 'div.umb-stylesheet-rules__listitem',
handle: '.handle',
tolerance: 'pointer',
update: function (e, ui) {
$scope.model.setDirty();
}
};
$scope.remove = function (parameter, evt) {
evt.preventDefault();
$scope.model.macro.parameters = _.without($scope.model.macro.parameters, parameter);
$scope.model.setDirty();
}
$scope.add = function (evt) {
evt.preventDefault();
openOverlay({}, 'Add parameter', (newParameter) => {
if (!$scope.model.macro.parameters) {
$scope.model.macro.parameters = [];
}
$scope.model.macro.parameters.push(newParameter);
$scope.model.setDirty();
});
}
$scope.edit = function (parameter, evt) {
evt.preventDefault();
openOverlay(parameter,'Edit parameter', (newParameter) => {
parameter.key = newParameter.key;
parameter.label = newParameter.label;
parameter.editor = newParameter.editor;
parameter.editor = newParameter.editor;
$scope.model.setDirty();
});
}
function openOverlay(parameter, title, onSubmit) {
const ruleDialog = {
title: title,
parameter: _.clone(parameter),
editors : $scope.model.parameterEditors,
view: "views/macros/infiniteeditors/parameter.html",
size: "small",
submit: function (model) {
onSubmit(model.parameter);
editorService.close();
},
close: function () {
editorService.close();
}
};
editorService.open(ruleDialog);
}
}
angular.module("umbraco").controller("Umbraco.Editors.Macros.ParametersController", MacrosParametersController);

View File

@@ -0,0 +1,32 @@
<umb-box>
<umb-box-content>
<div class="sub-view-columns">
<div class="sub-view-column-left">
<h5>Parameters</h5>
<small>Define the parameters hat should be available when using his macro</small>
</div>
<div class="sub-view-column-right">
<div class="umb-property-editor umb-stylesheet-rules form-horizontal" ng-controller="Umbraco.Editors.Macros.ParametersController" >
<div ui-sortable="sortableOptions" ng-model="model.macro.parameters">
<div class="umb-stylesheet-rules__listitem" ng-click="edit(parameter, $event)" ng-repeat="parameter in model.macro.parameters">
<i class="icon icon-navigation handle"></i>
<div class="umb-stylesheet-rules__left">
{{parameter.label}}
</div>
<div class="umb-stylesheet-rules__right">
<a class="umb-node-preview__action umb-node-preview__action--red" ng-click="remove(parameter, $event)"><localize key="general_remove">Remove</localize></a>
</div>
</div>
</div>
<umb-button
type="button"
button-style="info"
action="add($event)"
label-key="general_add">
</umb-button>
</div>
</div>
</div>
</umb-box-content>
</umb-box>

View File

@@ -0,0 +1,43 @@
<umb-box>
<umb-box-header title="Info"></umb-box-header>
<umb-box-content>
<umb-control-group label="Id">
{{model.macro.id}}<br/>
{{model.macro.key}}
</umb-control-group>
</umb-box-content>
</umb-box>
<umb-box>
<umb-box-header title="Macro partial view"></umb-box-header>
<umb-box-content>
<umb-control-group label="Macro partial view">
<select class="input-block-level" ng-model="model.macro.view" ng-options="i as i for i in model.views" required>
</select>
</umb-control-group>
</umb-box-content>
</umb-box>
<umb-box>
<umb-box-header title="Editor settings"></umb-box-header>
<umb-box-content>
<umb-control-group label="Use in rich text editor and the grid">
<umb-toggle checked="model.macro.useInEditor" on-click="model.toggle('useInEditor')" ></umb-toggle>
</umb-control-group>
<umb-control-group label="Render in rich text editor and the grid" ng-if="model.macro.useInEditor">
<umb-toggle checked="model.macro.renderInEditor && model.macro.useInEditor" on-click="model.toggle('renderInEditor')" ></umb-toggle>
</umb-control-group>
</umb-box-content>
</umb-box>
<umb-box>
<umb-box-header title="Cache settings"></umb-box-header>
<umb-box-content>
<umb-control-group label="Cache period">
<input type="number" ng-model="model.macro.cachePeriod"/>
</umb-control-group>
<umb-control-group label="Cache by page">
<umb-toggle checked="model.macro.cacheByPage" on-click="model.toggle('cacheByPage')"></umb-toggle>
</umb-control-group>
<umb-control-group label="Cache personalized">
<umb-toggle checked="model.macro.cacheByUser" on-click="model.toggle('cacheByUser')"></umb-toggle>
</umb-control-group>
</umb-box-content>
</umb-box>

View File

@@ -169,9 +169,13 @@ namespace Umbraco.Web.Editors
controller => controller.GetAllowedChildren(0))
},
{
"macroApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MacroController>(
"macroRenderingApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MacroRenderingController>(
controller => controller.GetMacroParameters(0))
},
{
"macroApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<MacrosController>(
controller => controller.Create(null))
},
{
"authenticationApiBaseUrl", _urlHelper.GetUmbracoApiServiceBaseUrl<AuthenticationController>(
controller => controller.PostLogin(null))

View File

@@ -27,11 +27,11 @@ namespace Umbraco.Web.Editors
/// Session, we don't want it to throw null reference exceptions.
/// </remarks>
[PluginController("UmbracoApi")]
public class MacroController : UmbracoAuthorizedJsonController, IRequiresSessionState
public class MacroRenderingController : UmbracoAuthorizedJsonController, IRequiresSessionState
{
private readonly IVariationContextAccessor _variationContextAccessor;
public MacroController(IVariationContextAccessor variationContextAccessor)
public MacroRenderingController(IVariationContextAccessor variationContextAccessor)
{
_variationContextAccessor = variationContextAccessor;
}

View File

@@ -0,0 +1,320 @@
namespace Umbraco.Web.Editors
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using Umbraco.Core;
using Umbraco.Core.IO;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Web.Composing;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Mvc;
using Umbraco.Web.UI;
using Umbraco.Web.WebApi;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
/// <summary>
/// The API controller used for editing dictionary items
/// </summary>
[PluginController("UmbracoApi")]
[UmbracoTreeAuthorize(Constants.Trees.Macros)]
public class MacrosController : BackOfficeNotificationsController
{
/// <summary>
/// Creates a new macro
/// </summary>
/// <param name="name">
/// The name.
/// </param>
/// <returns>
/// The <see cref="HttpResponseMessage"/>.
/// </returns>
[HttpPost]
public HttpResponseMessage Create(string name)
{
if (string.IsNullOrWhiteSpace(name))
{
return this.ReturnErrorResponse("Name can not be empty");
}
var alias = name.ToSafeAlias();
if (this.Services.MacroService.GetByAlias(alias) != null)
{
return this.ReturnErrorResponse("Macro with this alias already exists");
}
try
{
var macro = new Macro
{
Alias = alias,
Name = name,
MacroSource = string.Empty,
MacroType = MacroTypes.PartialView
};
this.Services.MacroService.Save(macro, this.Security.CurrentUser.Id);
return this.Request.CreateResponse(HttpStatusCode.OK, macro.Id);
}
catch (Exception exception)
{
return this.ReturnErrorResponse("Error creating macro", true, exception);
}
}
[HttpGet]
public HttpResponseMessage GetById(int id)
{
var macro = this.Services.MacroService.GetById(id);
if (macro == null)
{
return this.ReturnErrorResponse($"Macro with id {id} does not exist");
}
var macroDisplay = new MacroDisplay
{
Alias = macro.Alias,
Id = macro.Id,
Key = macro.Key,
Name = macro.Name,
CacheByPage = macro.CacheByPage,
CacheByUser = macro.CacheByMember,
CachePeriod = macro.CacheDuration,
View = macro.MacroSource,
RenderInEditor = !macro.DontRender,
UseInEditor = macro.UseInEditor,
Path = $"-1,{macro.Id}"
};
var parameters = new List<MacroParameterDisplay>();
foreach (var param in macro.Properties.Values.OrderBy(x => x.SortOrder))
{
parameters.Add(new MacroParameterDisplay
{
Editor = param.EditorAlias,
Key = param.Alias,
Label = param.Name,
Id = param.Id
});
}
macroDisplay.Parameters = parameters;
return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay);
}
[HttpPost]
public HttpResponseMessage Save(MacroDisplay macroDisplay)
{
if (macroDisplay == null)
{
return this.ReturnErrorResponse($"No macro data found in request");
}
var macro = this.Services.MacroService.GetById(int.Parse(macroDisplay.Id.ToString()));
if (macro == null)
{
return this.ReturnErrorResponse($"Macro with id {macroDisplay.Id} does not exist");
}
if (macroDisplay.Alias != macro.Alias)
{
var macroByAlias = this.Services.MacroService.GetByAlias(macroDisplay.Alias);
if (macroByAlias != null)
{
return this.ReturnErrorResponse("Macro with this alias already exists");
}
}
macro.Alias = macroDisplay.Alias;
macro.Name = macroDisplay.Name;
macro.CacheByMember = macroDisplay.CacheByUser;
macro.CacheByPage = macroDisplay.CacheByPage;
macro.CacheDuration = macroDisplay.CachePeriod;
macro.DontRender = !macroDisplay.RenderInEditor;
macro.UseInEditor = macroDisplay.UseInEditor;
macro.MacroSource = macroDisplay.View;
macro.MacroType = MacroTypes.PartialView;
macro.Properties.ReplaceAll(macroDisplay.Parameters.Select((x,i) => new MacroProperty(x.Key, x.Label, i, x.Editor)));
try
{
this.Services.MacroService.Save(macro, this.Security.CurrentUser.Id);
macroDisplay.Notifications.Clear();
macroDisplay.Notifications.Add(new Models.ContentEditing.Notification("Success", "Macro saved", SpeechBubbleIcon.Success));
return this.Request.CreateResponse(HttpStatusCode.OK, macroDisplay);
}
catch (Exception exception)
{
return this.ReturnErrorResponse("Error creating macro", true, exception);
}
}
/// <summary>
/// Gets a list of available macro partials
/// </summary>
/// <returns>
/// The <see cref="HttpResponseMessage"/>.
/// </returns>
public HttpResponseMessage GetPartialViews()
{
var views = new List<string>();
views.AddRange(this.FindPartialViewsFiles());
return this.Request.CreateResponse(HttpStatusCode.OK, views);
}
/// <summary>
/// Gets the available parameter editors
/// </summary>
/// <returns>
/// The <see cref="HttpResponseMessage"/>.
/// </returns>
public HttpResponseMessage GetParameterEditors()
{
return this.Request.CreateResponse(HttpStatusCode.OK, Current.ParameterEditors);
}
/// <summary>
/// Returns a error response and optionally logs it
/// </summary>
/// <param name="message">
/// The error message.
/// </param>
/// <param name="logError">
/// Value to indicate if the error needs to be logged
/// </param>
/// <param name="exception">
/// The exception to log
/// </param>
/// <returns>
/// The <see cref="HttpResponseMessage"/>.
/// </returns>
private HttpResponseMessage ReturnErrorResponse(string message, bool logError = false, Exception exception = null)
{
if (logError && exception != null)
{
this.Logger.Error<MacrosController>(exception, message);
}
return this.Request.CreateNotificationValidationErrorResponse(message);
}
/// <summary>
/// Finds all the macro partials
/// </summary>
/// <returns>
/// The <see cref="IEnumerable"/>.
/// </returns>
private IEnumerable<string> FindPartialViewsFiles()
{
var files = new List<string>();
files.AddRange(this.FindPartialViewFilesInViewsFolder());
files.AddRange(this.FindPartialViewFilesInPluginFolders());
return files;
}
/// <summary>
/// Finds all macro partials in the views folder
/// </summary>
/// <returns>
/// The <see cref="IEnumerable"/>.
/// </returns>
private IEnumerable<string> FindPartialViewFilesInViewsFolder()
{
var partialsDir = IOHelper.MapPath(SystemDirectories.MacroPartials);
return this.FindPartialViewFilesInFolder(
partialsDir,
partialsDir,
SystemDirectories.MacroPartials);
}
/// <summary>
/// Finds partial view files in app plugin folders.
/// </summary>
/// <returns>
/// The <see cref="IEnumerable"/>.
/// </returns>
private IEnumerable<string> FindPartialViewFilesInPluginFolders()
{
var files = new List<string>();
var appPluginsFolder = new DirectoryInfo(IOHelper.MapPath(SystemDirectories.AppPlugins));
if (!appPluginsFolder.Exists)
{
return files;
}
foreach (var directory in appPluginsFolder.GetDirectories())
{
var viewsFolder = directory.GetDirectories("Views");
if (viewsFolder.Any())
{
var macroPartials = viewsFolder.First().GetDirectories("MacroPartials");
if (macroPartials.Any())
{
files.AddRange(this.FindPartialViewFilesInFolder(macroPartials.First().FullName, macroPartials.First().FullName, SystemDirectories.AppPlugins + "/" + directory.Name + "/Views/MacroPartials"));
}
}
}
return files;
}
/// <summary>
/// Finds all partial views in a folder and subfolders
/// </summary>
/// <param name="orgPath">
/// The org path.
/// </param>
/// <param name="path">
/// The path.
/// </param>
/// <param name="prefixVirtualPath">
/// The prefix virtual path.
/// </param>
/// <returns>
/// The <see cref="IEnumerable"/>.
/// </returns>
private IEnumerable<string> FindPartialViewFilesInFolder(string orgPath, string path, string prefixVirtualPath)
{
var files = new List<string>();
var dirInfo = new DirectoryInfo(path);
foreach (var dir in dirInfo.GetDirectories())
{
files.AddRange(this.FindPartialViewFilesInFolder(orgPath, path + "/" + dir.Name, prefixVirtualPath));
}
var fileInfo = dirInfo.GetFiles("*.*");
files.AddRange(
fileInfo.Select(file =>
prefixVirtualPath.TrimEnd('/') + "/" + (path.Replace(orgPath, string.Empty).Trim('/') + "/" + file.Name).Trim('/')));
return files;
}
}
}

View File

@@ -0,0 +1,67 @@
using System.Collections.Generic;
using System.Runtime.Serialization;
namespace Umbraco.Web.Models.ContentEditing
{
/// <summary>
/// The macro display model
/// </summary>
[DataContract(Name = "dictionary", Namespace = "")]
public class MacroDisplay : EntityBasic, INotificationModel
{
/// <summary>
/// Initializes a new instance of the <see cref="MacroDisplay"/> class.
/// </summary>
public MacroDisplay()
{
this.Notifications = new List<Notification>();
this.Parameters = new List<MacroParameterDisplay>();
}
/// <inheritdoc />
[DataMember(Name = "notifications")]
public List<Notification> Notifications { get; }
/// <summary>
/// Gets or sets a value indicating whether the macro can be used in a rich text editor.
/// </summary>
[DataMember(Name = "useInEditor")]
public bool UseInEditor { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the macro should be rendered a rich text editor.
/// </summary>
[DataMember(Name = "renderInEditor")]
public bool RenderInEditor { get; set; }
/// <summary>
/// Gets or sets the cache period.
/// </summary>
[DataMember(Name = "cachePeriod")]
public int CachePeriod { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the macro should be cached by page
/// </summary>
[DataMember(Name = "cacheByPage")]
public bool CacheByPage { get; set; }
/// <summary>
/// Gets or sets a value indicating whether the macro should be cached by user
/// </summary>
[DataMember(Name = "cacheByUser")]
public bool CacheByUser { get; set; }
/// <summary>
/// Gets or sets the view.
/// </summary>
[DataMember(Name = "view")]
public string View { get; set; }
/// <summary>
/// Gets or sets the parameters.
/// </summary>
[DataMember(Name = "parameters")]
public IEnumerable<MacroParameterDisplay> Parameters { get; set; }
}
}

View File

@@ -0,0 +1,35 @@
namespace Umbraco.Web.Models.ContentEditing
{
using System.Runtime.Serialization;
/// <summary>
/// The macro parameter display.
/// </summary>
[DataContract(Name = "parameter", Namespace = "")]
public class MacroParameterDisplay
{
/// <summary>
/// Gets or sets the key.
/// </summary>
[DataMember(Name = "key")]
public string Key { get; set; }
/// <summary>
/// Gets or sets the label.
/// </summary>
[DataMember(Name = "label")]
public string Label { get; set; }
/// <summary>
/// Gets or sets the editor.
/// </summary>
[DataMember(Name = "editor")]
public string Editor { get; set; }
/// <summary>
/// Gets or sets the id.
/// </summary>
[DataMember(Name = "id")]
public int Id { get; set; }
}
}

View File

@@ -39,10 +39,7 @@ namespace Umbraco.Web.Trees
queryStrings,
macro.Name,
"icon-settings-alt",
false,
//TODO: Rebuild the macro editor in angular, then we dont need to have this at all (which is just a path to the legacy editor)
"/" + queryStrings.GetValue<string>("application") + "/framed/" +
Uri.EscapeDataString("/umbraco/developer/macros/editMacro.aspx?macroID=" + macro.Id)));
false));
}
}
@@ -56,11 +53,8 @@ namespace Umbraco.Web.Trees
if (id == Constants.System.Root.ToInvariantString())
{
//Create the normal create action
menu.Items.Add<ActionNew>(Services.TextService, opensDialog: true)
//Since we haven't implemented anything for macros in angular, this needs to be converted to
//use the legacy format
.ConvertLegacyMenuItem(null, "initmacros", queryStrings.GetValue<string>("application"));
menu.Items.Add<ActionNew>(Services.TextService);
//refresh action
menu.Items.Add(new RefreshNode(Services.TextService, true));

View File

@@ -125,6 +125,7 @@
<Compile Include="Editors\BackOfficePreviewModel.cs" />
<Compile Include="Editors\PackageController.cs" />
<Compile Include="Editors\KeepAliveController.cs" />
<Compile Include="Editors\MacrosController.cs" />
<Compile Include="Editors\RelationTypeController.cs" />
<Compile Include="Logging\WebProfiler.cs" />
<Compile Include="Logging\WebProfilerComponent.cs" />
@@ -166,6 +167,8 @@
<Compile Include="Media\TypeDetector\SvgDetector.cs" />
<Compile Include="Media\TypeDetector\TIFFDetector.cs" />
<Compile Include="Media\UploadAutoFillProperties.cs" />
<Compile Include="Models\ContentEditing\MacroDisplay.cs" />
<Compile Include="Models\ContentEditing\MacroParameterDisplay.cs" />
<Compile Include="Trees\BackOfficeSectionCollectionBuilder.cs" />
<Compile Include="Trees\MediaBackOfficeSection.cs" />
<Compile Include="Trees\MembersBackOfficeSection.cs" />
@@ -779,7 +782,7 @@
<Compile Include="Mvc\EnsurePublishedContentRequestAttribute.cs" />
<Compile Include="Mvc\UmbracoViewPage.cs" />
<Compile Include="Editors\LogController.cs" />
<Compile Include="Editors\MacroController.cs" />
<Compile Include="Editors\MacroRenderingController.cs" />
<Compile Include="Editors\MemberTypeController.cs" />
<Compile Include="Editors\UpdateCheckController.cs" />
<Compile Include="MembershipProviderExtensions.cs" />

View File

@@ -102,7 +102,7 @@ namespace umbraco
/// Initializes a new instance of the page for a content.
/// </summary>
/// <param name="content">The content.</param>
/// <remarks>This is for <see cref="MacroController"/> usage only.</remarks>
/// <remarks>This is for <see cref="MacroRenderingController"/> usage only.</remarks>
internal page(IContent content, IVariationContextAccessor variationContextAccessor)
: this(new PagePublishedContent(content, variationContextAccessor))
{ }