Add client side file for partial view editor

This commit is contained in:
Mads Rasmussen
2017-01-10 15:15:56 +01:00
parent 14e402878a
commit 845dd4673c
6 changed files with 532 additions and 27 deletions

View File

@@ -0,0 +1,57 @@
(function() {
'use strict';
function templateHelperService() {
//crappy hack due to dictionary items not in umbracoNode table
function getInsertDictionarySnippet(nodeName) {
return "@Umbraco.GetDictionaryValue(\"" + nodeName + "\")";
}
function getInsertPartialSnippet(nodeName) {
return "@Html.Partial(\"" + nodeName + "\")";
}
function getQuerySnippet(queryExpression) {
var code = "\n@{\n" + "\tvar selection = " + queryExpression + ";\n}\n";
code += "<ul>\n" +
"\t@foreach(var item in selection){\n" +
"\t\t<li>\n" +
"\t\t\t<a href=\"@item.Url\">@item.Name</a>\n" +
"\t\t</li>\n" +
"\t}\n" +
"</ul>\n\n";
return code;
}
function getRenderBodySnippet() {
return "@RenderBody()";
}
function getRenderSectionSnippet(sectionName, mandatory) {
return "@RenderSection(\"" + sectionName + "\", " + mandatory + ")";
}
function getAddSectionSnippet(sectionName) {
return "@section " + sectionName + "\r\n{\r\n\r\n\t{0}\r\n\r\n}\r\n";
}
////////////
var service = {
getInsertDictionarySnippet: getInsertDictionarySnippet,
getInsertPartialSnippet: getInsertPartialSnippet,
getQuerySnippet: getQuerySnippet,
getRenderBodySnippet: getRenderBodySnippet,
getRenderSectionSnippet: getRenderSectionSnippet,
getAddSectionSnippet: getAddSectionSnippet
};
return service;
}
angular.module('umbraco.services').factory('templateHelper', templateHelperService);
})();

View File

@@ -2,19 +2,19 @@
<div class="umb-insert-code-boxes">
<div class="umb-insert-code-box" ng-click="vm.openPageFieldOverlay()">
<div ng-if="model.allowedTypes.umbracoField" class="umb-insert-code-box" ng-click="vm.openPageFieldOverlay()">
<div class="umb-insert-code-box__title">Value</div>
<div class="umb-insert-code-box__description">Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values.</div>
</div>
<div class="umb-insert-code-box" ng-click="vm.openPartialOverlay()">
<div ng-if="model.allowedTypes.partial" class="umb-insert-code-box" ng-click="vm.openPartialOverlay()">
<div class="umb-insert-code-box__title">Partial view</div>
<div class="umb-insert-code-box__description">
A partial view is a separate template file which can be rendered inside another
template, it's great for reusing markup or for separating complex templates into separate files.</div>
</div>
<div class="umb-insert-code-box" ng-click="vm.openMacroPicker()">
<div ng-if="model.allowedTypes.macro" class="umb-insert-code-box" ng-click="vm.openMacroPicker()">
<div class="umb-insert-code-box__title">Macro</div>
<div class="umb-insert-code-box__description">
A Macro is a configurable component which is great for
@@ -23,7 +23,7 @@
</div>
</div>
<div class="umb-insert-code-box" ng-click="vm.openDictionaryItemOverlay()">
<div ng-if="model.allowedTypes.dictionary" class="umb-insert-code-box" ng-click="vm.openDictionaryItemOverlay()">
<div class="umb-insert-code-box__title">Dictionary</div>
<div class="umb-insert-code-box__description">A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites.</div>
</div>

View File

@@ -0,0 +1,315 @@
(function () {
"use strict";
function PartialViewsEditController($scope, $routeParams, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, angularHelper, $timeout, contentEditingHelper, localizationService, templateHelper) {
var vm = this;
var localizeSaving = localizationService.localize("general_saving");
vm.page = {};
vm.page.loading = true;
vm.partialView = {};
vm.partialViewPathPrefix = "Partials /";
//menu
vm.page.menu = {};
vm.page.menu.currentSection = appState.getSectionState("currentSection");
vm.page.menu.currentNode = null;
// bind functions to view model
vm.save = save;
vm.openPageFieldOverlay = openPageFieldOverlay;
vm.openDictionaryItemOverlay = openDictionaryItemOverlay;
vm.openQueryBuilderOverlay = openQueryBuilderOverlay;
vm.openMacroOverlay = openMacroOverlay;
vm.openInsertOverlay = openInsertOverlay;
/* Functions bound to view model */
function save() {
vm.page.saveButtonState = "busy";
vm.partialView.content = vm.editor.getValue();
contentEditingHelper.contentEditorPerformSave({
statusMessage: localizeSaving,
saveMethod: templateResource.save,
scope: $scope,
content: vm.partialView,
//We do not redirect on failure for stylesheets - this is because it is not possible to actually save the doc
// type when server side validation fails - as opposed to content where we are capable of saving the content
// item if server side validation fails
redirectOnFailure: false,
rebindCallback: function (orignal, saved) {}
}).then(function (saved) {
notificationsService.success("Partial View saved");
vm.page.saveButtonState = "success";
vm.partialView = saved;
//sync state
editorState.set(vm.partialView);
// normal tree sync
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// clear $dirty state on form
setFormState("pristine");
}, function (err) {
vm.page.saveButtonState = "error";
localizationService.localize("speechBubbles_validationFailedHeader").then(function (headerValue) {
localizationService.localize("speechBubbles_validationFailedMessage").then(function(msgValue) {
notificationsService.error(headerValue, msgValue);
});
});
});
}
function openInsertOverlay() {
vm.insertOverlay = {
view: "insert",
allowedTypes: {
macro: true,
dictionary: true,
umbracoField: true
},
hideSubmitButton: true,
show: true,
submit: function(model) {
switch(model.insert.type) {
case "macro":
var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc");
insert(macroObject.syntax);
break;
case "dictionary":
var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name);
insert(code);
break;
case "partial":
var code = templateHelper.getInsertPartialSnippet(model.insert.node.name);
insert(code);
break;
case "umbracoField":
insert(model.insert.umbracoField);
break;
}
vm.insertOverlay.show = false;
vm.insertOverlay = null;
},
close: function(oldModel) {
// close the dialog
vm.insertOverlay.show = false;
vm.insertOverlay = null;
// focus editor
vm.editor.focus();
}
};
}
function openMacroOverlay() {
vm.macroPickerOverlay = {
view: "macropicker",
dialogData: {},
show: true,
title: "Insert macro",
submit: function (model) {
var macroObject = macroService.collectValueData(model.selectedMacro, model.macroParams, "Mvc");
insert(macroObject.syntax);
vm.macroPickerOverlay.show = false;
vm.macroPickerOverlay = null;
},
close: function(oldModel) {
// close the dialog
vm.macroPickerOverlay.show = false;
vm.macroPickerOverlay = null;
// focus editor
vm.editor.focus();
}
};
}
function openPageFieldOverlay() {
vm.pageFieldOverlay = {
submitButtonLabel: "Insert",
closeButtonlabel: "Cancel",
view: "insertfield",
show: true,
submit: function (model) {
insert(model.umbracoField);
vm.pageFieldOverlay.show = false;
vm.pageFieldOverlay = null;
},
close: function (model) {
// close the dialog
vm.pageFieldOverlay.show = false;
vm.pageFieldOverlay = null;
// focus editor
vm.editor.focus();
}
};
}
function openDictionaryItemOverlay() {
vm.dictionaryItemOverlay = {
view: "treepicker",
section: "settings",
treeAlias: "dictionary",
entityType: "dictionary",
multiPicker: false,
show: true,
title: "Insert dictionary item",
select: function(node){
var code = templateHelper.getInsertDictionarySnippet(node.name);
insert(code);
vm.dictionaryItemOverlay.show = false;
vm.dictionaryItemOverlay = null;
},
close: function (model) {
// close dialog
vm.dictionaryItemOverlay.show = false;
vm.dictionaryItemOverlay = null;
// focus editor
vm.editor.focus();
}
};
}
function openQueryBuilderOverlay() {
vm.queryBuilderOverlay = {
view: "querybuilder",
show: true,
title: "Query for content",
submit: function (model) {
var code = templateHelper.getQuerySnippet(model.result.queryExpression);
insert(code);
vm.queryBuilderOverlay.show = false;
vm.queryBuilderOverlay = null;
},
close: function (model) {
// close dialog
vm.queryBuilderOverlay.show = false;
vm.queryBuilderOverlay = null;
// focus editor
vm.editor.focus();
}
};
}
/* Local functions */
function init() {
//we need to load this somewhere, for now its here.
assetsService.loadCss("lib/ace-razor-mode/theme/razor_chrome.css");
if ($routeParams.create) {
templateResource.getScaffold().then(function (partialView) {
ready(partialView);
});
} else {
templateResource.getById($routeParams.id).then(function (partialView) {
ready(partialView);
});
}
}
function ready(partialView) {
vm.page.loading = false;
vm.partialView = partialView;
//sync state
editorState.set(vm.partialView);
navigationService.syncTree({ tree: "partialViews", path: vm.partialView.path, forceReload: true }).then(function (syncArgs) {
vm.page.menu.currentNode = syncArgs.node;
});
// ace configuration
vm.aceOption = {
mode: "razor",
theme: "chrome",
showPrintMargin: false,
advanced: {
fontSize: '14px'
},
onLoad: function(_editor) {
vm.editor = _editor;
// initial cursor placement
// Keep cursor in name field if we are create a new template
// else set the cursor at the bottom of the code editor
if(!$routeParams.create) {
$timeout(function(){
vm.editor.navigateFileEnd();
vm.editor.focus();
persistCurrentLocation();
});
}
//change on blur, focus
vm.editor.on("blur", persistCurrentLocation);
vm.editor.on("focus", persistCurrentLocation);
}
}
}
function insert(str) {
vm.editor.moveCursorToPosition(vm.currentPosition);
vm.editor.insert(str);
vm.editor.focus();
// set form state to $dirty
setFormState("dirty");
}
function persistCurrentLocation() {
vm.currentPosition = vm.editor.getCursorPosition();
}
function setFormState(state) {
// get the current form
var currentForm = angularHelper.getCurrentForm($scope);
// set state
if(state === "dirty") {
currentForm.$setDirty();
} else if(state === "pristine") {
currentForm.$setPristine();
}
}
init();
}
angular.module("umbraco").controller("Umbraco.Editors.PartialViews.EditController", PartialViewsEditController);
})();

View File

@@ -0,0 +1,129 @@
<div ng-controller="Umbraco.Editors.PartialViews.EditController as vm">
<umb-load-indicator ng-if="vm.page.loading"></umb-load-indicator>
<form name="contentForm"
ng-submit="vm.save()"
novalidate
val-form-manager>
<umb-editor-view ng-if="!vm.page.loading">
<umb-editor-header
name="vm.partialView.name"
hide-alias="true"
description="vm.partialView.virtualPath"
menu="vm.page.menu"
hide-icon="true">
</umb-editor-header>
<umb-editor-container>
<div class="flex" style="margin-bottom: 30px;">
<div class="flex" style="margin-left: auto;">
<div class="btn-group umb-era-button-group dropdown" style="margin-right: 10px;">
<button
type="button"
class="umb-era-button umb-button--s"
ng-click="vm.openInsertOverlay()">
<i class="icon icon-add"></i> Insert
</button>
<a class="umb-era-button umb-button--s dropdown-toggle umb-button-group__toggle" data-toggle="dropdown">
<span class="caret"></span>
</a>
<ul aria-labelledby="dLabel" class="dropdown-menu bottom-up umb-button-group__sub-buttons" role="menu">
<li><a href="" ng-click="vm.openPageFieldOverlay()">Value</a></li>
<li><a href="" ng-click="vm.openMacroOverlay()">Macro</a></li>
<li><a href="" ng-click="vm.openDictionaryItemOverlay()">Dictionary</a></li>
</ul>
</div>
<button
type="button"
style="margin-right: 10px;"
class="umb-era-button umb-button--s"
ng-click="vm.openQueryBuilderOverlay()">
<i class="icon icon-wand"></i> Query builder
</button>
</div>
</div>
<div
auto-scale="85"
umb-ace-editor="vm.aceOption"
ng-model="vm.partialView.content">
</div>
</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>
<umb-overlay
ng-if="vm.insertOverlay.show"
model="vm.insertOverlay"
view="vm.insertOverlay.view"
position="right">
</umb-overlay>
<umb-overlay
ng-if="vm.macroPickerOverlay.show"
model="vm.macroPickerOverlay"
view="vm.macroPickerOverlay.view"
position="right">
</umb-overlay>
<umb-overlay
ng-if="vm.pageFieldOverlay.show"
model="vm.pageFieldOverlay"
position="right"
view="vm.pageFieldOverlay.view">
</umb-overlay>
<umb-overlay
ng-if="vm.dictionaryItemOverlay.show"
model="vm.dictionaryItemOverlay"
position="right"
view="vm.dictionaryItemOverlay.view">
</umb-overlay>
<umb-overlay
ng-if="vm.queryBuilderOverlay.show"
model="vm.queryBuilderOverlay"
position="right"
view="vm.queryBuilderOverlay.view">
</umb-overlay>
<umb-overlay
ng-if="vm.sectionsOverlay.show"
model="vm.sectionsOverlay"
view="vm.sectionsOverlay.view"
position="right">
</umb-overlay>
</div>

View File

@@ -1,7 +1,7 @@
(function () {
"use strict";
function TemplatesEditController($scope, $routeParams, $timeout, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, contentEditingHelper, localizationService, angularHelper) {
function TemplatesEditController($scope, $routeParams, $timeout, templateResource, assetsService, notificationsService, editorState, navigationService, appState, macroService, treeService, contentEditingHelper, localizationService, angularHelper, templateHelper) {
var vm = this;
var oldMasterTemplateAlias = null;
@@ -169,26 +169,30 @@
vm.insertOverlay = {
view: "insert",
allowedTypes: {
macro: true,
dictionary: true,
partial: true,
umbracoField: true
},
hideSubmitButton: true,
show: true,
submit: function(model) {
switch(model.insert.type) {
case "macro":
case "macro":
var macroObject = macroService.collectValueData(model.insert.selectedMacro, model.insert.macroParams, "Mvc");
insert(macroObject.syntax);
break;
case "dictionary":
//crappy hack due to dictionary items not in umbracoNode table
var code = "@Umbraco.GetDictionaryValue(\"" + model.insert.node.name + "\")";
var code = templateHelper.getInsertDictionarySnippet(model.insert.node.name);
insert(code);
break;
case "partial":
//crappy hack due to dictionary items not in umbracoNode table
var code = "@Html.Partial(\"" + model.insert.node.name + "\")";
var code = templateHelper.getInsertPartialSnippet(model.insert.node.name);
insert(code);
break;
@@ -272,8 +276,7 @@
show: true,
title: "Insert dictionary item",
select: function(node){
//crappy hack due to dictionary items not in umbracoNode table
var code = "@Umbraco.GetDictionaryValue(\"" + node.name + "\")";
var code = templateHelper.getInsertDictionarySnippet(node.name);
insert(code);
vm.dictionaryItemOverlay.show = false;
@@ -299,8 +302,8 @@
show: true,
title: "Insert Partial view",
select: function(node){
//crappy hack due to dictionary items not in umbracoNode table
var code = "@Html.Partial(\"" + node.name + "\")";
var code = templateHelper.getInsertPartialSnippet(node.name);
insert(code);
vm.partialItemOverlay.show = false;
@@ -321,18 +324,9 @@
view: "querybuilder",
show: true,
title: "Query for content",
submit: function (model) {
var code = "\n@{\n" + "\tvar selection = " + model.result.queryExpression + ";\n}\n";
code += "<ul>\n" +
"\t@foreach(var item in selection){\n" +
"\t\t<li>\n" +
"\t\t\t<a href=\"@item.Url\">@item.Name</a>\n" +
"\t\t</li>\n" +
"\t}\n" +
"</ul>\n\n";
var code = templateHelper.getQuerySnippet(model.result.queryExpression);
insert(code);
vm.queryBuilderOverlay.show = false;
@@ -360,15 +354,18 @@
submit: function(model) {
if (model.insertType === 'renderBody') {
insert("@RenderBody()");
var code = templateHelper.getRenderBodySnippet();
insert(code);
}
if (model.insertType === 'renderSection') {
insert("@RenderSection(\"" + model.renderSectionName + "\", " + model.mandatoryRenderSection + ")");
var code = templateHelper.getRenderSectionSnippet(model.renderSectionName, model.mandatoryRenderSection);
insert(code);
}
if (model.insertType === 'addSection') {
wrap("@section " + model.sectionName + "\r\n{\r\n\r\n\t{0}\r\n\r\n}\r\n");
var code = templateHelper.getAddSectionSnippet(model.sectionName);
wrap(code);
}
vm.sectionsOverlay.show = false;

View File

@@ -101,6 +101,13 @@
$setPristine: function() {}
}
}
},
templateHelper: {
getInsertDictionary: function() { return ""; },
getInsertPartialSnippet: function() { return ""; },
getQuerySnippet: function() { return ""; },
getRenderBodySnippet: function() { return ""; },
getRenderSectionSnippet: function() { return ""; }
}
});
}