Fixes data type pre-value issues with MNTP, removes unecessary contentpicker.html and memberpicker.html overlays since treepicker.html is all that should be used, cleans up the treepicker one to be more inline with our current angular standards. Removes uneeded event handling from treepicker

This commit is contained in:
Shannon
2018-04-12 21:29:36 +10:00
parent 80d682c22a
commit fddea8c815
23 changed files with 388 additions and 391 deletions

View File

@@ -44,6 +44,7 @@ namespace Umbraco.Core.PropertyEditors
Key = string.IsNullOrWhiteSpace(attribute.Key) ? property.Name : attribute.Key,
Name = attribute.Name,
PropertyName = property.Name,
PropertyType = property.PropertyType,
Description = attribute.Description,
HideLabel = attribute.HideLabel,
View = attribute.View
@@ -67,6 +68,7 @@ namespace Umbraco.Core.PropertyEditors
fields.Add(field);
field.PropertyName = property.Name;
field.PropertyType = property.PropertyType;
if (!string.IsNullOrWhiteSpace(attribute.Key))
field.Key = attribute.Key;
@@ -137,7 +139,25 @@ namespace Umbraco.Core.PropertyEditors
// only keep fields that have a non-null/empty value
// rest will fall back to default during ToObject()
if (editorValues.TryGetValue(field.Key, out var value) && value != null && (!(value is string stringValue) || !string.IsNullOrWhiteSpace(stringValue)))
o[field.PropertyName] = value is JToken jtoken ? jtoken : JToken.FromObject(value);
{
if (value is JToken jtoken)
{
//if it's a jtoken then set it
o[field.PropertyName] = jtoken;
}
else if (field.PropertyType == typeof(bool) && value is string sBool)
{
//if it's a boolean property type but a string is found, try to do a conversion
var converted = sBool.TryConvertTo<bool>();
if (converted)
o[field.PropertyName] = converted.Result;
}
else
{
//default behavior
o[field.PropertyName] = JToken.FromObject(value);
}
}
}
return o.ToObject<TConfiguration>();

View File

@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Umbraco.Core.IO;
@@ -63,6 +64,12 @@ namespace Umbraco.Core.PropertyEditors
[JsonIgnore]
public string PropertyName { get; set; }
/// <summary>
/// Gets or sets the property clr type of the field.
/// </summary>
[JsonIgnore]
public Type PropertyType { get; set; }
/// <summary>
/// Gets or sets the description of the field.
/// </summary>

View File

@@ -330,6 +330,19 @@
return false;
};
}
if (!Object.toBoolean) {
/** Converts a string/integer/bool to true/false */
Object.toBoolean = function (obj) {
if ((typeof obj) === "boolean") {
return obj;
}
if (obj === "1" || obj === 1 || obj === "true") {
return true;
}
return false;
};
}
})();
})();

View File

@@ -1,48 +0,0 @@
<div ng-controller="Umbraco.Overlays.TreePickerController" ng-init="init('content')">
<div ng-hide="miniListView">
<div class="umb-control-group">
<umb-tree-search-box
hide-search-callback="hideSearch"
search-callback="onSearchResults"
search-from-id="{{searchInfo.searchFromId}}"
search-from-name="{{searchInfo.searchFromName}}"
show-search="{{searchInfo.showSearch}}"
section="content">
</umb-tree-search-box>
</div>
<umb-tree-search-results
ng-if="searchInfo.showSearch"
results="searchInfo.results"
select-result-callback="selectResult">
</umb-tree-search-results>
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
<umb-tree
section="content"
treealias="content"
hideheader="{{hideHeader}}"
hideoptions="true"
isdialog="true"
customtreeparams="{{customTreeParams}}"
eventhandler="dialogTreeEventHandler"
enablelistviewsearch="true"
enablelistviewexpand="true"
enablecheckboxes="{{multiPicker}}">
</umb-tree>
</div>
</div>
<umb-mini-list-view
ng-if="miniListView"
node="miniListView"
entity-type="{{entityType}}"
start-node-id="model.startNodeId"
on-select="selectListViewNode(node)"
on-close="closeMiniListView()">
</umb-mini-list-view>
</div>

View File

@@ -1,47 +0,0 @@
<div ng-controller="Umbraco.Overlays.TreePickerController" ng-init="init('member')">
<div ng-hide="miniListView">
<div class="umb-control-group">
<umb-tree-search-box
hide-search-callback="hideSearch"
search-callback="onSearchResults"
search-from-id="{{searchInfo.searchFromId}}"
search-from-name="{{searchInfo.searchFromName}}"
show-search="{{searchInfo.showSearch}}"
section="member">
</umb-tree-search-box>
</div>
<umb-tree-search-results
ng-if="searchInfo.showSearch"
results="searchInfo.results"
select-result-callback="selectResult">
</umb-tree-search-results>
<div ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
<umb-tree
section="member"
treealias="member"
hideheader="{{hideHeader}}"
hideoptions="true"
isdialog="true"
customtreeparams="{{customTreeParams}}"
eventhandler="dialogTreeEventHandler"
enablelistviewsearch="true"
enablelistviewexpand="true"
enablecheckboxes="{{multiPicker}}">
</umb-tree>
</div>
</div>
<umb-mini-list-view
ng-if="miniListView"
node="miniListView"
entity-type="{{entityType}}"
start-node-id="model.startNodeId"
on-select="selectListViewNode(node)"
on-close="closeMiniListView()">
</umb-mini-list-view>
</div>

View File

@@ -82,7 +82,9 @@
function chooseSource(query) {
vm.contentPickerOverlay = {
view: "contentpicker",
view: "treepicker",
section: "content",
treeAlias: "content",
show: true,
submit: function(model) {
@@ -210,4 +212,4 @@
angular.module("umbraco").controller("Umbraco.Overlays.QueryBuilderController", QueryBuilderOverlayController);
})();
})();

View File

@@ -14,175 +14,177 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
mediaResource,
memberResource,
languageResource) {
var tree = null;
var dialogOptions = $scope.model;
$scope.treeReady = false;
$scope.dialogTreeEventHandler = $({});
$scope.section = dialogOptions.section;
$scope.treeAlias = dialogOptions.treeAlias;
$scope.multiPicker = dialogOptions.multiPicker;
$scope.hideHeader = (typeof dialogOptions.hideHeader) === "boolean" ? dialogOptions.hideHeader : true;
//used as the result selection
$scope.model.selection = [];
//the tree object when it loads
var tree = null;
// Search and listviews is only working for content, media and member section
var searchableSections = ["content", "media", "member"];
var vm = this;
vm.treeReady = false;
vm.dialogTreeEventHandler = $({});
vm.section = $scope.model.section;
vm.treeAlias = $scope.model.treeAlias;
vm.multiPicker = $scope.model.multiPicker;
vm.hideHeader = (typeof $scope.model.hideHeader) === "boolean" ? $scope.model.hideHeader : true;
// if you need to load a not initialized tree set this value to false - default is true
$scope.onlyInitialized = dialogOptions.onlyInitialized;
$scope.searchInfo = {
searchFromId: dialogOptions.startNodeId,
vm.onlyInitialized = $scope.model.onlyInitialized;
vm.searchInfo = {
searchFromId: $scope.model.startNodeId,
searchFromName: null,
showSearch: false,
results: [],
selectedSearchResults: []
}
$scope.model.selection = [];
}
vm.startNodeId = $scope.model.startNodeId;
//Used for toggling an empty-state message
//Some trees can have no items (dictionary & forms email templates)
$scope.hasItems = true;
$scope.emptyStateMessage = dialogOptions.emptyStateMessage;
vm.hasItems = true;
vm.emptyStateMessage = $scope.model.emptyStateMessage;
vm.languages = [];
vm.selectedLanguage = {};
vm.languageSelectorIsOpen = false;
vm.showLanguageSelector = $scope.model.showLanguageSelector;
// Allow the entity type to be passed in but defaults to Document for backwards compatibility.
vm.entityType = $scope.model.entityType ? $scope.model.entityType : "Document";
vm.enableSearh = searchableSections.indexOf(vm.section) !== -1;
vm.toggleLanguageSelector = toggleLanguageSelector;
vm.selectLanguage = selectLanguage;
vm.onSearchResults = onSearchResults;
vm.hideSearch = hideSearch;
vm.closeMiniListView = closeMiniListView;
vm.selectListViewNode = selectListViewNode;
/**
* Performs the initialization of this component
*/
function onInit () {
// load languages
languageResource.getAll().then(function (languages) {
vm.languages = languages;
$scope.languages = [];
$scope.selectedLanguage = {};
$scope.page = {};
$scope.page.languageSelectorIsOpen = false;
var evts = [];
// Listen for language updates
evts.push(eventsService.on("editors.languages.languageDeleted",
function (e, args) {
languageResource.getAll().then(function (languages) {
$scope.languages = languages;
// set the default language
vm.languages.forEach(function (language) {
if (language.isDefault) {
vm.selectedLanguage = language;
vm.languageSelectorIsOpen = false;
}
});
}));
});
evts.push(eventsService.on("editors.languages.languageCreated",
function (e, args) {
languageResource.getAll().then(function (languages) {
$scope.languages = languages;
});
}));
// load languages
languageResource.getAll().then(function (languages) {
$scope.languages = languages;
// select the default language
$scope.languages.forEach(function (language) {
if (language.isDefault) {
$scope.selectLanguage(language);
}
});
});
$scope.selectLanguage = function (language) {
$scope.selectedLanguage = language;
// close the language selector
$scope.page.languageSelectorIsOpen = false;
};
$scope.toggleLanguageSelector = function () {
$scope.page.languageSelectorIsOpen = !$scope.page.languageSelectorIsOpen;
};
//This is called from ng-init
//it turns out it is called from the angular html : / Have a look at views/common / overlays / contentpicker / contentpicker.html you'll see ng-init.
//this is probably an anti pattern IMO and shouldn't be used
$scope.init = function (contentType) {
if (contentType === "content") {
$scope.entityType = "Document";
if (vm.treeAlias === "content") {
vm.entityType = "Document";
if (!$scope.model.title) {
$scope.model.title = localizationService.localize("defaultdialogs_selectContent");
}
} else if (contentType === "member") {
$scope.entityType = "Member";
}
else if (vm.treeAlias === "member" || vm.section) {
vm.entityType = "Member";
if (!$scope.model.title) {
$scope.model.title = localizationService.localize("defaultdialogs_selectMember");
}
} else if (contentType === "media") {
$scope.entityType = "Media";
}
else if (vm.treeAlias === "media" || vm.section === "media") {
vm.entityType = "Media";
if (!$scope.model.title) {
$scope.model.title = localizationService.localize("defaultdialogs_selectMedia");
}
}
//var searchText = "Search...";
//localizationService.localize("general_search").then(function (value) {
// searchText = value + "...";
//});
//min / max values
//TODO: Where are these used?
if ($scope.model.minNumber) {
$scope.model.minNumber = parseInt($scope.model.minNumber, 10);
}
if ($scope.model.maxNumber) {
$scope.model.maxNumber = parseInt($scope.model.maxNumber, 10);
}
}
var searchText = "Search...";
localizationService.localize("general_search").then(function (value) {
searchText = value + "...";
});
// Allow the entity type to be passed in but defaults to Document for backwards compatibility.
$scope.entityType = dialogOptions.entityType ? dialogOptions.entityType : "Document";
//min / max values
if (dialogOptions.minNumber) {
dialogOptions.minNumber = parseInt(dialogOptions.minNumber, 10);
}
if (dialogOptions.maxNumber) {
dialogOptions.maxNumber = parseInt(dialogOptions.maxNumber, 10);
}
if (dialogOptions.section === "member") {
$scope.entityType = "Member";
} else if (dialogOptions.section === "media") {
$scope.entityType = "Media";
}
// Search and listviews is only working for content, media and member section
var searchableSections = ["content", "media", "member"];
$scope.enableSearh = searchableSections.indexOf($scope.section) !== -1;
//if a alternative startnode is used, we need to check if it is a container
if ($scope.enableSearh &&
dialogOptions.startNodeId &&
dialogOptions.startNodeId !== -1 &&
dialogOptions.startNodeId !== "-1") {
entityResource.getById(dialogOptions.startNodeId, $scope.entityType).then(function (node) {
if (node.metaData.IsContainer) {
openMiniListView(node);
}
//if a alternative startnode is used, we need to check if it is a container
if (vm.enableSearh &&
vm.startNodeId &&
vm.startNodeId !== -1 &&
vm.startNodeId !== "-1") {
entityResource.getById(vm.startNodeId, vm.entityType).then(function (node) {
if (node.metaData.IsContainer) {
openMiniListView(node);
}
initTree();
});
}
else {
initTree();
});
} else {
initTree();
}
}
//Configures filtering
if (dialogOptions.filter) {
//Configures filtering
if ($scope.model.filter) {
dialogOptions.filterExclude = false;
dialogOptions.filterAdvanced = false;
//used advanced filtering
if (angular.isFunction(dialogOptions.filter)) {
dialogOptions.filterAdvanced = true;
} else if (angular.isObject(dialogOptions.filter)) {
dialogOptions.filterAdvanced = true;
} else {
if (dialogOptions.filter.startsWith("!")) {
dialogOptions.filterExclude = true;
dialogOptions.filter = dialogOptions.filter.substring(1);
}
$scope.model.filterExclude = false;
$scope.model.filterAdvanced = false;
//used advanced filtering
if (dialogOptions.filter.startsWith("{")) {
dialogOptions.filterAdvanced = true;
//convert to object
dialogOptions.filter = angular.fromJson(dialogOptions.filter);
if (angular.isFunction($scope.model.filter)) {
$scope.model.filterAdvanced = true;
}
else if (angular.isObject($scope.model.filter)) {
$scope.model.filterAdvanced = true;
}
else {
if ($scope.model.filter.startsWith("!")) {
$scope.model.filterExclude = true;
$scope.model.filter = $scope.model.filter.substring(1);
}
//used advanced filtering
if ($scope.model.filter.startsWith("{")) {
$scope.model.filterAdvanced = true;
//convert to object
$scope.model.filter = angular.fromJson($scope.model.filter);
}
}
}
}
/**
* Updates the tree's query parameters
*/
function initTree() {
//create the custom query string param for this tree
$scope.customTreeParams = dialogOptions.startNodeId ? "startNodeId=" + dialogOptions.startNodeId : "";
$scope.customTreeParams += dialogOptions.customTreeParams ? "&" + dialogOptions.customTreeParams : "";
$scope.treeReady = true;
//create the custom query string param for this tree
var queryParams = {};
if (vm.startNodeId) {
queryParams["startNodeId"] = $scope.model.startNodeId;
}
if (vm.selectedLanguage && vm.selectedLanguage.id) {
queryParams["languageId"] = vm.selectedLanguage.id;
}
var queryString = $.param(queryParams); //create the query string from the params object
vm.customTreeParams = queryString + (queryString ? "&" : "") + ($scope.model.customTreeParams ? $scope.model.customTreeParams : "");
vm.treeReady = true;
}
function selectLanguage(language) {
vm.selectedLanguage = language;
// close the language selector
vm.languageSelectorIsOpen = false;
initTree();
};
function toggleLanguageSelector() {
vm.languageSelectorIsOpen = !vm.languageSelectorIsOpen;
};
function nodeExpandedHandler(ev, args) {
@@ -199,7 +201,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
//now we need to look in the already selected search results and
// toggle the check boxes for those ones that are listed
var exists = _.find($scope.searchInfo.selectedSearchResults,
var exists = _.find(vm.searchInfo.selectedSearchResults,
function (selected) {
return child.id == selected.id;
});
@@ -216,7 +218,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
//gets the tree object when it loads
function treeLoadedHandler(ev, args) {
//args.tree contains children (args.tree.root.children)
$scope.hasItems = args.tree.root.children.length > 0;
vm.hasItems = args.tree.root.children.length > 0;
tree = args.tree;
}
@@ -240,11 +242,12 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
});
//remove it from the custom tracked search result list
$scope.searchInfo.selectedSearchResults = _.reject($scope.searchInfo.selectedSearchResults,
vm.searchInfo.selectedSearchResults = _.reject(vm.searchInfo.selectedSearchResults,
function (i) {
return i.id == args.node.id;
});
} else {
}
else {
eventsService.emit("dialogs.treePickerController.select", args);
if (args.node.filtered) {
@@ -255,7 +258,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
//from the server in this method.
if ($scope.model.select) {
$scope.model.select(args.node);
} else {
}
else {
select(args.node.name, args.node.id);
//toggle checked state
args.node.selected = args.node.selected === true ? false : true;
@@ -276,40 +280,46 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
name: text
};
if ($scope.multiPicker) {
if (vm.multiPicker) {
if (entity) {
multiSelectItem(entity);
} else {
}
else {
multiSelectItem(rootNode);
}
} else {
}
else {
$scope.model.selection.push(rootNode);
$scope.model.submit($scope.model);
}
} else {
}
else {
if ($scope.multiPicker) {
if (vm.multiPicker) {
if (entity) {
multiSelectItem(entity);
} else {
}
else {
//otherwise we have to get it from the server
entityResource.getById(id, $scope.entityType).then(function (ent) {
entityResource.getById(id, vm.entityType).then(function (ent) {
multiSelectItem(ent);
});
}
} else {
}
else {
$scope.hideSearch();
hideSearch();
//if an entity has been passed in, use it
if (entity) {
$scope.model.selection.push(entity);
$scope.model.submit($scope.model);
} else {
}
else {
//otherwise we have to get it from the server
entityResource.getById(id, $scope.entityType).then(function (ent) {
entityResource.getById(id, vm.entityType).then(function (ent) {
$scope.model.selection.push(ent);
$scope.model.submit($scope.model);
});
@@ -335,7 +345,8 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
if (found) {
$scope.model.selection.splice(foundIndex, 1);
} else {
}
else {
$scope.model.selection.push(item);
}
@@ -343,7 +354,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
function performFiltering(nodes) {
if (!dialogOptions.filter) {
if (!$scope.model.filter) {
return;
}
@@ -354,52 +365,57 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
return !angular.isObject(n.metaData.listViewNode);
});
if (dialogOptions.filterAdvanced) {
if ($scope.model.filterAdvanced) {
//filter either based on a method or an object
var filtered = angular.isFunction(dialogOptions.filter)
? _.filter(nodes, dialogOptions.filter)
: _.where(nodes, dialogOptions.filter);
var filtered = angular.isFunction($scope.model.filter)
? _.filter(nodes, $scope.model.filter)
: _.where(nodes, $scope.model.filter);
angular.forEach(filtered,
function (value, key) {
value.filtered = true;
if (dialogOptions.filterCssClass) {
if ($scope.model.filterCssClass) {
if (!value.cssClasses) {
value.cssClasses = [];
}
value.cssClasses.push(dialogOptions.filterCssClass);
value.cssClasses.push($scope.model.filterCssClass);
}
});
} else {
var a = dialogOptions.filter.toLowerCase().replace(/\s/g, '').split(',');
}
else {
var a = $scope.model.filter.toLowerCase().replace(/\s/g, '').split(',');
angular.forEach(nodes,
function (value, key) {
var found = a.indexOf(value.metaData.contentType.toLowerCase()) >= 0;
if (!dialogOptions.filterExclude && !found || dialogOptions.filterExclude && found) {
if (!$scope.model.filterExclude && !found || $scope.model.filterExclude && found) {
value.filtered = true;
if (dialogOptions.filterCssClass) {
if ($scope.model.filterCssClass) {
if (!value.cssClasses) {
value.cssClasses = [];
}
value.cssClasses.push(dialogOptions.filterCssClass);
value.cssClasses.push($scope.model.filterCssClass);
}
}
});
}
}
function openMiniListView(node) {
vm.miniListView = node;
}
$scope.multiSubmit = function (result) {
entityResource.getByIds(result, $scope.entityType).then(function (ents) {
function multiSubmit(result) {
entityResource.getByIds(result, vm.entityType).then(function (ents) {
$scope.submit(ents);
});
};
}
/** method to select a search result */
$scope.selectResult = function (evt, result) {
function selectResult(evt, result) {
if (result.filtered) {
return;
@@ -412,9 +428,10 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
//add/remove to our custom tracked list of selected search results
if (result.selected) {
$scope.searchInfo.selectedSearchResults.push(result);
} else {
$scope.searchInfo.selectedSearchResults = _.reject($scope.searchInfo.selectedSearchResults,
vm.searchInfo.selectedSearchResults.push(result);
}
else {
vm.searchInfo.selectedSearchResults = _.reject(vm.searchInfo.selectedSearchResults,
function (i) {
return i.id == result.id;
});
@@ -427,10 +444,9 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
found.selected = result.selected;
}
}
}
};
$scope.hideSearch = function () {
function hideSearch() {
//Traverse the entire displayed tree and update each node to sync with the selected search results
if (tree) {
@@ -441,7 +457,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
_.each(children,
function (child) {
//check if the id is in the selection, if so ensure it's flagged as selected
var exists = _.find($scope.searchInfo.selectedSearchResults,
var exists = _.find(vm.searchInfo.selectedSearchResults,
function (selected) {
return child.id == selected.id;
});
@@ -470,7 +486,7 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
return c === 'tree-node-slide-up-hide-active';
});
var listViewResults = _.filter($scope.searchInfo.selectedSearchResults,
var listViewResults = _.filter(vm.searchInfo.selectedSearchResults,
function (i) {
return i.parentId == child.id;
});
@@ -510,13 +526,13 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
}
$scope.searchInfo.showSearch = false;
$scope.searchInfo.searchFromId = dialogOptions.startNodeId;
$scope.searchInfo.searchFromName = null;
$scope.searchInfo.results = [];
vm.searchInfo.showSearch = false;
vm.searchInfo.searchFromId = vm.startNodeId;
vm.searchInfo.searchFromName = null;
vm.searchInfo.results = [];
}
$scope.onSearchResults = function (results) {
function onSearchResults(results) {
//filter all items - this will mark an item as filtered
performFiltering(results);
@@ -527,10 +543,10 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
return !item.filtered;
});
$scope.searchInfo.results = results;
vm.searchInfo.results = results;
//sync with the curr selected results
_.each($scope.searchInfo.results,
_.each(vm.searchInfo.results,
function (result) {
var exists = _.find($scope.model.selection,
function (selectedId) {
@@ -541,40 +557,31 @@ angular.module("umbraco").controller("Umbraco.Overlays.TreePickerController",
}
});
$scope.searchInfo.showSearch = true;
};
$scope.dialogTreeEventHandler.bind("treeLoaded", treeLoadedHandler);
$scope.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
$scope.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
$scope.$on('$destroy',
function () {
$scope.dialogTreeEventHandler.unbind("treeLoaded", treeLoadedHandler);
$scope.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
$scope.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
});
$scope.selectListViewNode = function (node) {
vm.searchInfo.showSearch = true;
}
function selectListViewNode(node) {
select(node.name, node.id);
//toggle checked state
node.selected = node.selected === true ? false : true;
};
$scope.closeMiniListView = function () {
$scope.miniListView = undefined;
};
function openMiniListView(node) {
$scope.miniListView = node;
}
//ensure to unregister from all events!
function closeMiniListView() {
vm.miniListView = undefined;
}
vm.dialogTreeEventHandler.bind("treeLoaded", treeLoadedHandler);
vm.dialogTreeEventHandler.bind("treeNodeExpanded", nodeExpandedHandler);
vm.dialogTreeEventHandler.bind("treeNodeSelect", nodeSelectHandler);
$scope.$on('$destroy',
function () {
for (var e in evts) {
eventsService.unsubscribe(evts[e]);
}
vm.dialogTreeEventHandler.unbind("treeLoaded", treeLoadedHandler);
vm.dialogTreeEventHandler.unbind("treeNodeExpanded", nodeExpandedHandler);
vm.dialogTreeEventHandler.unbind("treeNodeSelect", nodeSelectHandler);
});
//initialize
onInit();
});

View File

@@ -1,59 +1,59 @@
<div ng-controller="Umbraco.Overlays.TreePickerController">
<div ng-controller="Umbraco.Overlays.TreePickerController as vm">
<div ng-hide="miniListView">
<div class="umb-language-picker" ng-if="languages.length > 1" on-outside-click="page.languageSelectorIsOpen = false" style="padding-bottom: 5px">
<div class="umb-language-picker__toggle" ng-click="toggleLanguageSelector()">
<div>{{selectedLanguage.name}}</div>
<ins class="umb-language-picker__expand" ng-class="{'icon-navigation-down': !page.languageSelectorIsOpen, 'icon-navigation-up': page.languageSelectorIsOpen}" class="icon-navigation-right">&nbsp;</ins>
<div class="umb-language-picker" ng-if="vm.showLanguageSelector && vm.languages.length > 1" on-outside-click="vm.page.languageSelectorIsOpen = false" style="padding-bottom: 5px">
<div class="umb-language-picker__toggle" ng-click="vm.toggleLanguageSelector()">
<div>{{vm.selectedLanguage.name}}</div>
<ins class="umb-language-picker__expand" ng-class="{'icon-navigation-down': !vm.languageSelectorIsOpen, 'icon-navigation-up': vm.languageSelectorIsOpen}" class="icon-navigation-right">&nbsp;</ins>
</div>
<div class="umb-language-picker__dropdown" ng-if="page.languageSelectorIsOpen">
<a ng-click="selectLanguage(language)" ng-repeat="language in languages" href="">{{language.name}}</a>
<div class="umb-language-picker__dropdown" ng-if="vm.languageSelectorIsOpen">
<a ng-click="vm.selectLanguage(language)" ng-repeat="language in vm.languages" href="">{{language.name}}</a>
</div>
</div>
<div class="umb-control-group">
<umb-tree-search-box ng-if="enableSearh"
hide-search-callback="hideSearch"
search-callback="onSearchResults"
search-from-id="{{searchInfo.searchFromId}}"
search-from-name="{{searchInfo.searchFromName}}"
show-search="{{searchInfo.showSearch}}"
section="{{section}}">
<umb-tree-search-box ng-if="vm.enableSearh"
hide-search-callback="vm.hideSearch"
search-callback="vm.onSearchResults"
search-from-id="{{vm.searchInfo.searchFromId}}"
search-from-name="{{vm.searchInfo.searchFromName}}"
show-search="{{vm.searchInfo.showSearch}}"
section="{{vm.section}}">
</umb-tree-search-box>
</div>
<umb-tree-search-results ng-if="searchInfo.showSearch"
results="searchInfo.results"
select-result-callback="selectResult">
<umb-tree-search-results ng-if="vm.searchInfo.showSearch"
results="vm.searchInfo.results"
select-result-callback="vm.selectResult">
</umb-tree-search-results>
<umb-empty-state ng-if="!hasItems && emptyStateMessage" position="center">
{{ emptyStateMessage }}
<umb-empty-state ng-if="!vm.hasItems && vm.emptyStateMessage" position="center">
{{ vm.emptyStateMessage }}
</umb-empty-state>
<div ng-if="treeReady" ng-hide="searchInfo.showSearch" ng-animate="'tree-fade-out'">
<umb-tree section="{{section}}"
treealias="{{treeAlias}}"
hideheader="{{hideHeader}}"
<div ng-if="vm.treeReady" ng-hide="vm.searchInfo.showSearch" ng-animate="'tree-fade-out'">
<umb-tree section="{{vm.section}}"
treealias="{{vm.treeAlias}}"
hideheader="{{vm.hideHeader}}"
hideoptions="true"
isdialog="true"
onlyinitialized="{{onlyInitialized}}"
customtreeparams="{{customTreeParams}}"
eventhandler="dialogTreeEventHandler"
onlyinitialized="{{vm.onlyInitialized}}"
customtreeparams="{{vm.customTreeParams}}"
eventhandler="vm.dialogTreeEventHandler"
enablelistviewsearch="true"
enablelistviewexpand="true"
enablecheckboxes="{{multiPicker}}">
enablecheckboxes="{{vm.multiPicker}}">
</umb-tree>
</div>
</div>
<umb-mini-list-view ng-if="miniListView"
node="miniListView"
entity-type="{{entityType}}"
start-node-id="model.startNodeId"
on-select="selectListViewNode(node)"
on-close="closeMiniListView()">
<umb-mini-list-view ng-if="vm.miniListView"
node="vm.miniListView"
entity-type="{{vm.entityType}}"
start-node-id="vm.startNodeId"
on-select="vm.selectListViewNode(node)"
on-close="vm.closeMiniListView()">
</umb-mini-list-view>
</div>

View File

@@ -6,10 +6,11 @@
* @description
* The controller for creating content blueprints
*/
function ContentBlueprintCreateController($scope, $location, contentTypeResource, navigationService) {
function ContentBlueprintCreateController($scope, $location, contentTypeResource, navigationService, appState) {
var vm = this;
var node = $scope.dialogOptions.currentNode;
var section = appState.getSectionState("currentSection");
vm.createBlueprint = createBlueprint;
@@ -25,7 +26,7 @@ function ContentBlueprintCreateController($scope, $location, contentTypeResource
}
function createBlueprint(documentType) {
$location.path("/settings/contentBlueprints/edit/" + node.id).search("create", "true").search("doctype", documentType.alias);
$location.path("/" + section + "/contentBlueprints/edit/" + node.id).search("create", "true").search("doctype", documentType.alias);
navigationService.hideMenu();
}

View File

@@ -14,6 +14,7 @@ function DataTypeCreateController($scope, $location, navigationService, dataType
};
var node = $scope.dialogOptions.currentNode;
var section = appState.getSectionState("currentSection");
$scope.showCreateFolder = function() {
$scope.model.creatingFolder = true;
@@ -29,8 +30,6 @@ function DataTypeCreateController($scope, $location, navigationService, dataType
formHelper.resetForm({ scope: $scope });
var section = appState.getSectionState("currentSection");
}, function(err) {
//TODO: Handle errors
@@ -40,7 +39,7 @@ function DataTypeCreateController($scope, $location, navigationService, dataType
$scope.createDataType = function() {
$location.search('create', null);
$location.path("/developer/datatypes/edit/" + node.id).search("create", "true");
$location.path("/" + section + "/datatypes/edit/" + node.id).search("create", "true");
navigationService.hideMenu();
}
}

View File

@@ -14,7 +14,7 @@ function MemberTypesCreateController($scope, $location, navigationService, membe
};
var node = $scope.dialogOptions.currentNode;
var section = appState.getSectionState("currentSection");
$scope.showCreateFolder = function() {
$scope.model.creatingFolder = true;
@@ -33,8 +33,6 @@ function MemberTypesCreateController($scope, $location, navigationService, membe
formHelper.resetForm({ scope: $scope });
var section = appState.getSectionState("currentSection");
}, function(err) {
//TODO: Handle errors
@@ -44,7 +42,7 @@ function MemberTypesCreateController($scope, $location, navigationService, membe
$scope.createMemberType = function() {
$location.search('create', null);
$location.path("/settings/membertypes/edit/" + node.id).search("create", "true");
$location.path("/" + section + "/membertypes/edit/" + node.id).search("create", "true");
navigationService.hideMenu();
}
}

View File

@@ -14,6 +14,7 @@ function mediaPickerController($scope, dialogService, entityResource, $log, icon
$scope.sortable = false;
var dialogOptions = {
view: "treepicker",
multiPicker: false,
entityType: "Media",
section: "media",
@@ -28,7 +29,6 @@ function mediaPickerController($scope, dialogService, entityResource, $log, icon
$scope.openContentPicker = function() {
$scope.contentPickerOverlay = dialogOptions;
$scope.contentPickerOverlay.view = "treePicker";
$scope.contentPickerOverlay.show = true;
$scope.contentPickerOverlay.submit = function(model) {
@@ -117,4 +117,4 @@ function mediaPickerController($scope, dialogService, entityResource, $log, icon
}
angular.module('umbraco').controller("Umbraco.PrevalueEditors.MediaPickerController",mediaPickerController);
angular.module('umbraco').controller("Umbraco.PrevalueEditors.MediaPickerController",mediaPickerController);

View File

@@ -11,7 +11,8 @@ angular.module('umbraco')
$scope.allowEdit = true;
$scope.sortable = false;
var config = {
var config = {
view: "treepicker",
multiPicker: false,
entityType: "Document",
type: "content",
@@ -52,7 +53,6 @@ angular.module('umbraco')
$scope.openContentPicker = function () {
$scope.treePickerOverlay = config;
$scope.treePickerOverlay.section = config.type;
$scope.treePickerOverlay.view = "treePicker";
$scope.treePickerOverlay.show = true;
$scope.treePickerOverlay.submit = function (model) {

View File

@@ -1,6 +1,18 @@
//this controller simply tells the dialogs service to open a mediaPicker window
//with a specified callback, this callback will receive an object with a selection on it
/**
* The controller that is used for a couple different Property Editors: Multi Node Tree Picker, Content Picker,
* since this is used by MNTP and it supports content, media and members, there is code to deal with all 3 of those types
* @param {any} $scope
* @param {any} entityResource
* @param {any} editorState
* @param {any} iconHelper
* @param {any} $routeParams
* @param {any} angularHelper
* @param {any} navigationService
* @param {any} $location
* @param {any} miniEditorHelper
* @param {any} localizationService
*/
function contentPickerController($scope, entityResource, editorState, iconHelper, $routeParams, angularHelper, navigationService, $location, miniEditorHelper, localizationService) {
var unsubscribe;
@@ -89,10 +101,10 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
}
//Umbraco persists boolean for prevalues as "0" or "1" so we need to convert that!
$scope.model.config.multiPicker = ($scope.model.config.multiPicker === "1" ? true : false);
$scope.model.config.showOpenButton = ($scope.model.config.showOpenButton === "1" ? true : false);
$scope.model.config.showEditButton = ($scope.model.config.showEditButton === "1" ? true : false);
$scope.model.config.showPathOnHover = ($scope.model.config.showPathOnHover === "1" ? true : false);
$scope.model.config.multiPicker = Object.toBoolean($scope.model.config.multiPicker);
$scope.model.config.showOpenButton = Object.toBoolean($scope.model.config.showOpenButton);
$scope.model.config.showEditButton = Object.toBoolean($scope.model.config.showEditButton);
$scope.model.config.showPathOnHover = Object.toBoolean($scope.model.config.showPathOnHover);
var entityType = $scope.model.config.startNode.type === "member"
? "Member"
@@ -105,6 +117,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
//the dialog options for the picker
var dialogOptions = {
view: "treepicker",
multiPicker: $scope.model.config.multiPicker,
entityType: entityType,
filterCssClass: "not-allowed not-published",
@@ -128,7 +141,7 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
//since most of the pre-value config's are used in the dialog options (i.e. maxNumber, minNumber, etc...) we'll merge the
// pre-value config on to the dialog options
angular.extend(dialogOptions, $scope.model.config);
//We need to manually handle the filter for members here since the tree displayed is different and only contains
// searchable list views
if (entityType === "Member") {
@@ -155,7 +168,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
return false;
}
}
if ($routeParams.section === "settings" && $routeParams.tree === "documentTypes") {
//if the content-picker is being rendered inside the document-type editor, we don't need to process the startnode query
dialogOptions.startNodeId = -1;
@@ -173,7 +185,6 @@ function contentPickerController($scope, entityResource, editorState, iconHelper
//dialog
$scope.openContentPicker = function() {
$scope.contentPickerOverlay = dialogOptions;
$scope.contentPickerOverlay.view = "treepicker";
$scope.contentPickerOverlay.show = true;
$scope.contentPickerOverlay.submit = function(model) {

View File

@@ -10,7 +10,8 @@ function memberPickerController($scope, dialogService, entityResource, $log, ico
$scope.renderModel = [];
$scope.allowRemove = true;
var dialogOptions = {
var dialogOptions = {
view: "treepicker",
multiPicker: false,
entityType: "Member",
section: "member",
@@ -40,7 +41,6 @@ function memberPickerController($scope, dialogService, entityResource, $log, ico
$scope.openMemberPicker = function() {
$scope.memberPickerOverlay = dialogOptions;
$scope.memberPickerOverlay.view = "memberPicker";
$scope.memberPickerOverlay.show = true;
$scope.memberPickerOverlay.submit = function(model) {

View File

@@ -21,11 +21,14 @@
$scope.internal = function($event) {
$scope.currentEditLink = null;
$scope.contentPickerOverlay = {};
$scope.contentPickerOverlay.view = "contentpicker";
$scope.contentPickerOverlay.multiPicker = false;
$scope.contentPickerOverlay.show = true;
$scope.contentPickerOverlay.idType = $scope.model.config.idType ? $scope.model.config.idType : "int";
$scope.contentPickerOverlay = {
view: "treepicker",
section: "content",
treeAlias: "content",
multiPicker: false,
show: true,
idType: $scope.model.config.idType ? $scope.model.config.idType : "int"
};
$scope.contentPickerOverlay.submit = function(model) {
@@ -46,12 +49,15 @@
$scope.selectInternal = function ($event, link) {
$scope.currentEditLink = link;
$scope.contentPickerOverlay = {};
$scope.contentPickerOverlay.view = "contentpicker";
$scope.contentPickerOverlay.multiPicker = false;
$scope.contentPickerOverlay.show = true;
$scope.contentPickerOverlay.idType = $scope.model.config.idType ? $scope.model.config.idType : "int";
$scope.contentPickerOverlay = {
view: "treepicker",
section: "content",
treeAlias: "content",
multiPicker: false,
show: true,
idType: $scope.model.config.idType ? $scope.model.config.idType : "int"
};
$scope.contentPickerOverlay.submit = function(model) {
select(model.selection[0]);

View File

@@ -124,7 +124,9 @@
function openContentPicker() {
vm.contentPicker = {
title: vm.labels.selectContentStartNode,
view: "contentpicker",
view: "treepicker",
section: "content",
treeAlias: "content",
hideSubmitButton: true,
hideHeader: false,
show: true,
@@ -215,7 +217,9 @@
function openGranularPermissionsPicker() {
vm.contentPicker = {
title: vm.labels.selectNode,
view: "contentpicker",
view: "treepicker",
section: "content",
treeAlias: "content",
hideSubmitButton: true,
show: true,
submit: function (model) {

View File

@@ -230,7 +230,9 @@
function openContentPicker() {
vm.contentPicker = {
title: vm.labels.selectContentStartNode,
view: "contentpicker",
view: "treepicker",
section: "content",
treeAlias: "content",
multiPicker: true,
selection: vm.user.startContentIds,
hideHeader: false,

View File

@@ -1,4 +1,5 @@
using Umbraco.Core.PropertyEditors;
using Newtonsoft.Json.Linq;
using Umbraco.Core.PropertyEditors;
namespace Umbraco.Web.PropertyEditors
{
@@ -8,7 +9,7 @@ namespace Umbraco.Web.PropertyEditors
public class MultiNodePickerConfiguration
{
[ConfigurationField("startNode", "Node type", "treesource")]
public string TreeSource { get; set; }
public MultiNodePickerConfigurationTreeSource TreeSource { get; set; }
[ConfigurationField("filter", "Allow items of type", "textstring", Description = "Separate with comma")]
public string Filter { get; set; }
@@ -19,7 +20,7 @@ namespace Umbraco.Web.PropertyEditors
[ConfigurationField("maxNumber", "Maximum number of items", "number")]
public int MaxNumber { get; set; }
[ConfigurationField("showOpenButtom", "Show open button (this feature is in preview!)", "boolean", Description = "Opens the node in a dialog")]
[ConfigurationField("showOpenButton", "Show open button (this feature is in preview!)", "boolean", Description = "Opens the node in a dialog")]
public bool ShowOpen { get; set; }
}
}
}

View File

@@ -23,7 +23,7 @@ namespace Umbraco.Web.PropertyEditors
// sanitize configuraiton
var output = base.ToConfigurationEditor(configuration);
output["multiPicker"] = configuration.MaxNumber > 1 ? "1" : "0";
output["multiPicker"] = configuration.MaxNumber > 1 ? true : false;
return output;
}
@@ -39,4 +39,4 @@ namespace Umbraco.Web.PropertyEditors
return d;
}
}
}
}

View File

@@ -0,0 +1,20 @@
using Newtonsoft.Json;
namespace Umbraco.Web.PropertyEditors
{
/// <summary>
/// Represents the 'startNode' value for the <see cref="MultiNodePickerConfiguration"/>
/// </summary>
[JsonObject]
public class MultiNodePickerConfigurationTreeSource
{
[JsonProperty("type")]
public string ObjectType {get;set;}
[JsonProperty("query")]
public string StartNodeQuery {get;set;}
[JsonProperty("id")]
public int? StartNodeId {get;set;}
}
}

View File

@@ -17,7 +17,7 @@ using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Trees
{
[UmbracoTreeAuthorize(Constants.Trees.DataTypes)]
[Tree(Constants.Applications.Developer, Constants.Trees.DataTypes, null, sortOrder:1)]
[Tree(Constants.Applications.Settings, Constants.Trees.DataTypes, null, sortOrder:1)]
[PluginController("UmbracoTrees")]
[CoreTree]
public class DataTypeTreeController : TreeController, ISearchableTree

View File

@@ -313,6 +313,7 @@
<Compile Include="PropertyEditors\MemberPickerConfiguration.cs" />
<Compile Include="PropertyEditors\MultiNodePickerConfiguration.cs" />
<Compile Include="PropertyEditors\MultiNodePickerConfigurationEditor.cs" />
<Compile Include="PropertyEditors\MultiNodePickerConfigurationTreeSource.cs" />
<Compile Include="PropertyEditors\MultiNodeTreePickerPropertyEditor.cs" />
<Compile Include="PropertyEditors\MultipleTestStringConfiguration.cs" />
<Compile Include="PropertyEditors\MultipleTextStringConfigurationEditor.cs" />