Updates lots of trees for searching and gets results grouped

This commit is contained in:
Shannon
2017-05-31 12:25:05 +02:00
parent 3ae16e717c
commit a70c9226fc
26 changed files with 1091 additions and 439 deletions

View File

@@ -116,7 +116,6 @@
<ItemGroup>
<None Include="App.config" />
<None Include="packages.config" />
<None Include="_TraceEventProgrammersGuide.docx" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Umbraco.Core\Umbraco.Core.csproj">
@@ -136,10 +135,6 @@
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.Analyzers.dll" />
<Analyzer Include="..\packages\Microsoft.CodeAnalysis.Analyzers.1.1.0\analyzers\dotnet\cs\Microsoft.CodeAnalysis.CSharp.Analyzers.dll" />
</ItemGroup>
<ItemGroup>
<Content Include="TraceEvent.ReadMe.txt" />
<Content Include="TraceEvent.ReleaseNotes.txt" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>

View File

@@ -120,8 +120,12 @@ angular.module('umbraco.services')
return entityResource.searchAll(args.term, args.canceler).then(function (data) {
_.each(data, function (resultByType) {
//we need to format the search result data to include things like the subtitle, urls, etc...
// this is done with registered angular services as part of the SearchableTreeAttribute, if that
// is not found, than we format with the default formatter
var formatterMethod = searchResultFormatter.configureDefaultResult;
//check if a custom formatter is specified...
if (resultByType.jsSvc) {
var searchFormatterService = $injector.get(resultByType.jsSvc);
if (searchFormatterService) {
@@ -135,10 +139,11 @@ angular.module('umbraco.services')
}
}
}
//now apply the formatter for each result
_.each(resultByType.results, function (item) {
formatterMethod.apply(this, [item, resultByType.treeAlias, resultByType.appAlias]);
});
});
});
return data;

View File

@@ -0,0 +1,37 @@
function searchResultFormatter(umbRequestHelper) {
function configureDefaultResult(content, treeAlias, appAlias) {
content.editorPath = appAlias + "/" + treeAlias + "/edit/" + content.id;
angular.extend(content.metaData, { treeAlias: treeAlias });
}
function configureContentResult(content, treeAlias, appAlias) {
content.menuUrl = umbRequestHelper.getApiUrl("contentTreeBaseUrl", "GetMenu", [{ id: content.id }, { application: appAlias }]);
content.editorPath = appAlias + "/" + treeAlias + "/edit/" + content.id;
angular.extend(content.metaData, { treeAlias: treeAlias });
content.subTitle = content.metaData.Url;
}
function configureMemberResult(member, treeAlias, appAlias) {
member.menuUrl = umbRequestHelper.getApiUrl("memberTreeBaseUrl", "GetMenu", [{ id: member.id }, { application: appAlias }]);
member.editorPath = appAlias + "/" + treeAlias + "/edit/" + (member.key ? member.key : member.id);
angular.extend(member.metaData, { treeAlias: treeAlias });
member.subTitle = member.metaData.Email;
}
function configureMediaResult(media, treeAlias, appAlias) {
media.menuUrl = umbRequestHelper.getApiUrl("mediaTreeBaseUrl", "GetMenu", [{ id: media.id }, { application: appAlias }]);
media.editorPath = appAlias + "/" + treeAlias + "/edit/" + media.id;
angular.extend(media.metaData, { treeAlias: treeAlias });
}
return {
configureContentResult: configureContentResult,
configureMemberResult: configureMemberResult,
configureMediaResult: configureMediaResult,
configureDefaultResult: configureDefaultResult
};
}
angular.module('umbraco.services').factory('searchResultFormatter', searchResultFormatter);

View File

@@ -15,21 +15,21 @@ function SearchController($scope, searchService, $log, $location, navigationServ
$scope.selectedResult = -1;
$scope.navigateResults = function(ev){
$scope.navigateResults = function (ev) {
//38: up 40: down, 13: enter
switch(ev.keyCode){
switch (ev.keyCode) {
case 38:
iterateResults(true);
iterateResults(true);
break;
case 40:
iterateResults(false);
iterateResults(false);
break;
case 13:
if ($scope.selectedItem) {
$location.path($scope.selectedItem.editorPath);
navigationService.hideSearch();
}
}
break;
}
};
@@ -39,50 +39,50 @@ function SearchController($scope, searchService, $log, $location, navigationServ
var groupIndex = -1;
var itemIndex = -1;
$scope.selectedItem = undefined;
function iterateResults(up){
function iterateResults(up) {
//default group
if(!group){
if (!group) {
group = $scope.groups[0];
groupIndex = 0;
}
if(up){
if(itemIndex === 0){
if(groupIndex === 0){
gotoGroup($scope.groups.length-1, true);
}else{
gotoGroup(groupIndex-1, true);
if (up) {
if (itemIndex === 0) {
if (groupIndex === 0) {
gotoGroup($scope.groups.length - 1, true);
} else {
gotoGroup(groupIndex - 1, true);
}
}else{
gotoItem(itemIndex-1);
} else {
gotoItem(itemIndex - 1);
}
}else{
if(itemIndex < group.results.length-1){
gotoItem(itemIndex+1);
}else{
if(groupIndex === $scope.groups.length-1){
} else {
if (itemIndex < group.results.length - 1) {
gotoItem(itemIndex + 1);
} else {
if (groupIndex === $scope.groups.length - 1) {
gotoGroup(0);
}else{
gotoGroup(groupIndex+1);
} else {
gotoGroup(groupIndex + 1);
}
}
}
}
function gotoGroup(index, up){
function gotoGroup(index, up) {
groupIndex = index;
group = $scope.groups[groupIndex];
if(up){
gotoItem(group.results.length-1);
}else{
gotoItem(0);
if (up) {
gotoItem(group.results.length - 1);
} else {
gotoItem(0);
}
}
function gotoItem(index){
function gotoItem(index) {
itemIndex = index;
$scope.selectedItem = group.results[itemIndex];
}
@@ -91,7 +91,7 @@ function SearchController($scope, searchService, $log, $location, navigationServ
var canceler = null;
$scope.$watch("searchTerm", _.debounce(function (newVal, oldVal) {
$scope.$apply(function() {
$scope.$apply(function () {
if ($scope.searchTerm) {
if (newVal !== null && newVal !== undefined && newVal !== oldVal) {
$scope.isSearching = true;
@@ -107,8 +107,16 @@ function SearchController($scope, searchService, $log, $location, navigationServ
canceler = $q.defer();
}
searchService.searchAll({ term: $scope.searchTerm, canceler: canceler }).then(function(result) {
$scope.groups = _.filter(result, function (group) { return group.results.length > 0; });
searchService.searchAll({ term: $scope.searchTerm, canceler: canceler }).then(function (result) {
//result is a dictionary of group Title and it's results
var filtered = {};
_.each(result, function (value, key) {
if (value.results.length > 0) {
filtered[key] = value;
}
});
$scope.groups = filtered;
//set back to null so it can be re-created
canceler = null;
});

View File

@@ -53,22 +53,21 @@
border-color: @turquoise-d1;
}
.umb-tree li.root > div {
padding: 0;
.umb-tree li.root > div:first-child {
padding: 0;
}
.umb-tree li.root > div h5 {
margin: 0;
width: 100%;
display: flex;
align-items: center;
.umb-tree li.root > div h5, .umb-tree li.root > div h6 {
margin: 0;
width: 100%;
display: flex;
align-items: center;
}
.umb-tree li.root > div h5 > a, .umb-tree-header {
display: flex;
padding: 20px 0 20px 20px;
box-sizing: border-box;
.umb-tree li.root > div:first-child h5 > a, .umb-tree-header {
display: flex;
padding: 20px 0 20px 20px;
box-sizing: border-box;
}
.umb-tree * {
@@ -96,14 +95,22 @@
text-decoration: none
}
.umb-tree div {
/*.umb-tree div.tree-node {
padding: 5px 0 5px 0;
position: relative;
overflow: hidden;
display: flex;
flex-wrap: nowrap;
align-items: center;
}*/
.umb-tree div {
padding: 5px 0 5px 0;
position: relative;
overflow: hidden;
display: flex;
flex-wrap: nowrap;
align-items: center;
}
.umb-tree a.noSpr {
@@ -192,6 +199,21 @@
/*color:@turquoise;*/
}
.umb-tree div.umb-search-group {
position: inherit;
display: inherit;
}
.umb-tree div.umb-search-group:hover {
background: inherit;
}
.umb-tree div.umb-search-group h6 {
/*color: @gray-5;*/
padding: 10px 0 10px 20px;
font-weight: inherit;
background:@gray-10;
}
.umb-tree .umb-search-group-item {
padding-left: 20px;
}

View File

@@ -1,104 +1,106 @@
<div id="leftcolumn" ng-controller="Umbraco.NavigationController"
ng-mouseleave="leaveTree($event)" ng-mouseenter="enterTree($event)">
ng-mouseleave="leaveTree($event)" ng-mouseenter="enterTree($event)">
<umb-sections sections="sections" ng-if="authenticated">
</umb-sections>
<umb-sections sections="sections" ng-if="authenticated">
</umb-sections>
<!-- navigation container -->
<div id="navigation" ng-show="showNavigation" class="fill umb-modalcolumn" ng-animate="'slide'" nav-resize>
<!-- navigation container -->
<div id="navigation" ng-show="showNavigation" class="fill umb-modalcolumn" ng-animate="'slide'" nav-resize>
<div ng-swipe-left="nav.hideNavigation()" class="navigation-inner-container span6">
<div ng-swipe-left="nav.hideNavigation()" class="navigation-inner-container span6">
<!-- the search -->
<div ng-controller="Umbraco.SearchController" ng-if="authenticated">
<!-- the search -->
<div ng-controller="Umbraco.SearchController" ng-if="authenticated">
<!-- Search form -->
<div id="search-form">
<div class="umb-modalcolumn-header">
<!-- Search form -->
<div id="search-form">
<div class="umb-modalcolumn-header">
<form class="form-search" novalidate>
<i class="icon-search"></i>
<input type="text"
hotkey="ctrl+space"
id="search-field"
ng-model="searchTerm"
class="umb-search-field search-query search-input"
localize="placeholder"
placeholder="@placeholders_search"
ng-keydown="navigateResults($event)"/>
</form>
<form class="form-search" novalidate>
<i class="icon-search"></i>
<input type="text"
hotkey="ctrl+space"
id="search-field"
ng-model="searchTerm"
class="umb-search-field search-query search-input"
localize="placeholder"
placeholder="@placeholders_search"
ng-keydown="navigateResults($event)" />
</form>
</div>
</div>
<!-- Search results -->
<div id="search-results" class="umb-modalcolumn-body" ng-show="showSearchResults">
<ul class="umb-tree">
<li class="root">
<div>
<h5 class="umb-tree-header"><localize key="general_searchResults">Search results</localize></h5>
</div>
<div class="umb-search-group" ng-repeat="(key, group) in groups">
<h6 class="umb-tree-header">{{key}}</h6>
<ul>
<li ng-repeat="result in group.results" ng-class="{'current':selectedItem == result}">
<div class="umb-search-group-item">
<a class="umb-search-group-item-link" ng-class="{'first':$first}" ng-click="searchHide()" ng-href="#/{{result.editorPath}}">
<div class="umb-search-group-item-name">
<i class="icon umb-tree-icon sprTree {{result.icon}}"></i>
{{result.name}}
</div>
<small class="search-subtitle" ng-show="result.subTitle">
{{result.subTitle}}
</small>
</a>
<a href ng-hide="!result.menuUrl" ng-click="searchShowMenu($event, {node: result})" class="umb-options"><i></i><i></i><i></i></a>
</div>
</div>
</li>
</ul>
<!-- Search results -->
<div id="search-results" class="umb-modalcolumn-body" ng-show="showSearchResults">
</div>
<ul class="umb-tree">
<li class="root">
<div>
<h5 class="umb-tree-header"><localize key="general_searchResults">Search results</localize></h5>
</div>
<ul class="umb-search-group" ng-repeat="group in groups">
<li ng-repeat="result in group.results" ng-class="{'current':selectedItem == result}">
<div class="umb-search-group-item">
<a class="umb-search-group-item-link" ng-class="{'first':$first}" ng-click="searchHide()" ng-href="#/{{result.editorPath}}">
<div class="umb-search-group-item-name">
<i class="icon umb-tree-icon sprTree {{result.icon}}"></i>
{{result.name}}
</div>
<small class="search-subtitle" ng-show="result.subTitle">
{{result.subTitle}}
</small>
</a>
<a href ng-click="searchShowMenu($event, {node: result})" class="umb-options"><i></i><i></i><i></i></a>
</div>
</li>
</ul>
</li>
</ul>
</div>
</div>
<!-- the tree -->
<div id="tree" class="umb-modalcolumn-body" ng-if="authenticated">
<umb-tree
cachekey="_"
eventhandler="treeEventHandler"
section="{{currentSection}}" >
</umb-tree>
</div>
</li>
</ul>
</div>
<div class="offset6" id="navOffset" style="z-index: 10">
</div>
<!-- The context menu -->
<div id='contextMenu' class="umb-modalcolumn fill shadow" ng-swipe-left="nav.hideMenu()" ng-show="showContextMenu" ng-animate="'slide'">
<umb-context-menu
menu-dialog-title="{{menuDialogTitle}}"
current-section="{{currentSection}}"
current-node="menuNode"
menu-actions="menuActions">
</umb-context-menu>
</div>
<!-- Tree dialogs -->
<div id="dialog" class='umb-modalcolumn fill shadow'
ng-swipe-left="nav.hideDialog()"
ng-show="showContextMenuDialog" ng-animate="'slide'">
<div class='umb-modalcolumn-header'>
<h1>{{menuDialogTitle}}</h1>
</div>
<div class='umb-modalcolumn-body'>
</div>
</div>
</div>
<!-- the tree -->
<div id="tree" class="umb-modalcolumn-body" ng-if="authenticated">
<umb-tree cachekey="_"
eventhandler="treeEventHandler"
section="{{currentSection}}">
</umb-tree>
</div>
</div>
<div class="offset6" id="navOffset" style="z-index: 10">
<!-- The context menu -->
<div id='contextMenu' class="umb-modalcolumn fill shadow" ng-swipe-left="nav.hideMenu()" ng-show="showContextMenu" ng-animate="'slide'">
<umb-context-menu menu-dialog-title="{{menuDialogTitle}}"
current-section="{{currentSection}}"
current-node="menuNode"
menu-actions="menuActions">
</umb-context-menu>
</div>
<!-- Tree dialogs -->
<div id="dialog" class='umb-modalcolumn fill shadow'
ng-swipe-left="nav.hideDialog()"
ng-show="showContextMenuDialog" ng-animate="'slide'">
<div class='umb-modalcolumn-header'>
<h1>{{menuDialogTitle}}</h1>
</div>
<div class='umb-modalcolumn-body'>
</div>
</div>
</div>
</div>
</div>

View File

@@ -55,13 +55,17 @@
<key alias="domainExists">Domain '%0%' has already been assigned</key>
<key alias="domainUpdated">Domain '%0%' has been updated</key>
<key alias="orEdit">Edit Current Domains</key>
<key alias="domainHelp"><![CDATA[Valid domain names are: "example.com", "www.example.com", "example.com:8080" or
<key alias="domainHelp">
<![CDATA[Valid domain names are: "example.com", "www.example.com", "example.com:8080" or
"https://www.example.com/". One-level paths in domains are supported, eg. "example.com/en". However, they
should be avoided. Better use the culture setting above.]]></key>
should be avoided. Better use the culture setting above.]]>
</key>
<key alias="inherit">Inherit</key>
<key alias="setLanguage">Culture</key>
<key alias="setLanguageHelp"><![CDATA[Set the culture for nodes below the current node,<br /> or inherit culture from parent nodes. Will also apply<br />
to the current node, unless a domain below applies too.]]></key>
<key alias="setLanguageHelp">
<![CDATA[Set the culture for nodes below the current node,<br /> or inherit culture from parent nodes. Will also apply<br />
to the current node, unless a domain below applies too.]]>
</key>
<key alias="setDomains">Domains</key>
</area>
<area alias="auditTrails">
@@ -181,7 +185,7 @@
<area alias="media">
<key alias="clickToUpload">Click to upload</key>
<key alias="dropFilesHere">Drop your files here...</key>
<key alias="urls">Link to media</key>
<key alias="urls">Link to media</key>
<key alias="orClickHereToUpload">or click here to choose files</key>
<key alias="onlyAllowedFiles">Only allowed file types are</key>
<key alias="maxFileSize">Max file size is</key>
@@ -290,10 +294,14 @@
<key alias="siterepublishHelp">The website cache will be refreshed. All published content will be updated, while unpublished content will stay unpublished.</key>
<key alias="tableColumns">Number of columns</key>
<key alias="tableRows">Number of rows</key>
<key alias="templateContentAreaHelp"><![CDATA[<strong>Set a placeholder id</strong> by setting an ID on your placeholder you can inject content into this template from child templates,
by referring this ID using a <code>&lt;asp:content /&gt;</code> element.]]></key>
<key alias="templateContentPlaceHolderHelp"><![CDATA[<strong>Select a placeholder id</strong> from the list below. You can only
choose Id's from the current template's master.]]></key>
<key alias="templateContentAreaHelp">
<![CDATA[<strong>Set a placeholder id</strong> by setting an ID on your placeholder you can inject content into this template from child templates,
by referring this ID using a <code>&lt;asp:content /&gt;</code> element.]]>
</key>
<key alias="templateContentPlaceHolderHelp">
<![CDATA[<strong>Select a placeholder id</strong> from the list below. You can only
choose Id's from the current template's master.]]>
</key>
<key alias="thumbnailimageclickfororiginal">Click on the image to see full size</key>
<key alias="treepicker">Pick item</key>
<key alias="viewCacheItem">View Cache Item</key>
@@ -325,9 +333,11 @@
<key alias="selectEditor">Select editor</key>
</area>
<area alias="dictionaryItem">
<key alias="description"><![CDATA[
<key alias="description">
<![CDATA[
Edit the different language versions for the dictionary item '<em>%0%</em>' below<br/>You can add additional languages under the 'languages' in the menu on the left
]]></key>
]]>
</key>
<key alias="displayName">Culture Name</key>
<key alias="changeKey">Edit the key of the dictionary item.</key>
<key alias="changeKeyError">
@@ -547,32 +557,32 @@
</area>
<area alias="shortcuts">
<key alias="addTab">Add tab</key>
<key alias="addProperty">Add property</key>
<key alias="addEditor">Add editor</key>
<key alias="addTemplate">Add template</key>
<key alias="addChildNode">Add child node</key>
<key alias="addChild">Add child</key>
<key alias="addTab">Add tab</key>
<key alias="addProperty">Add property</key>
<key alias="addEditor">Add editor</key>
<key alias="addTemplate">Add template</key>
<key alias="addChildNode">Add child node</key>
<key alias="addChild">Add child</key>
<key alias="editDataType">Edit data type</key>
<key alias="editDataType">Edit data type</key>
<key alias="navigateSections">Navigate sections</key>
<key alias="navigateSections">Navigate sections</key>
<key alias="shortcut">Shortcuts</key>
<key alias="showShortcuts">show shortcuts</key>
<key alias="shortcut">Shortcuts</key>
<key alias="showShortcuts">show shortcuts</key>
<key alias="toggleListView">Toggle list view</key>
<key alias="toggleAllowAsRoot">Toggle allow as root</key>
<key alias="toggleListView">Toggle list view</key>
<key alias="toggleAllowAsRoot">Toggle allow as root</key>
<key alias="commentLine">Comment/Uncomment lines</key>
<key alias="removeLine">Remove line</key>
<key alias="copyLineUp">Copy Lines Up</key>
<key alias="copyLineDown">Copy Lines Down</key>
<key alias="moveLineUp">Move Lines Up</key>
<key alias="moveLineDown">Move Lines Down</key>
<key alias="generalHeader">General</key>
<key alias="editorHeader">Editor</key>
<key alias="commentLine">Comment/Uncomment lines</key>
<key alias="removeLine">Remove line</key>
<key alias="copyLineUp">Copy Lines Up</key>
<key alias="copyLineDown">Copy Lines Down</key>
<key alias="moveLineUp">Move Lines Up</key>
<key alias="moveLineDown">Move Lines Down</key>
<key alias="generalHeader">General</key>
<key alias="editorHeader">Editor</key>
</area>
<area alias="graphicheadline">
@@ -591,34 +601,45 @@
<key alias="databaseErrorWebConfig">Could not save the web.config file. Please modify the connection string manually.</key>
<key alias="databaseFound">Your database has been found and is identified as</key>
<key alias="databaseHeader">Database configuration</key>
<key alias="databaseInstall"><![CDATA[
<key alias="databaseInstall">
<![CDATA[
Press the <strong>install</strong> button to install the Umbraco %0% database
]]></key>
]]>
</key>
<key alias="databaseInstallDone"><![CDATA[Umbraco %0% has now been copied to your database. Press <strong>Next</strong> to proceed.]]></key>
<key alias="databaseNotFound"><![CDATA[<p>Database not found! Please check that the information in the "connection string" of the "web.config" file is correct.</p>
<key alias="databaseNotFound">
<![CDATA[<p>Database not found! Please check that the information in the "connection string" of the "web.config" file is correct.</p>
<p>To proceed, please edit the "web.config" file (using Visual Studio or your favourite text editor), scroll to the bottom, add the connection string for your database in the key named "UmbracoDbDSN" and save the file. </p>
<p>
Click the <strong>retry</strong> button when
done.<br /><a href="http://our.umbraco.org/documentation/Using-Umbraco/Config-files/webconfig7" target="_blank">
More information on editing web.config here.</a></p>]]></key>
<key alias="databaseText"><![CDATA[To complete this step, you must know some information regarding your database server ("connection string").<br />
More information on editing web.config here.</a></p>]]>
</key>
<key alias="databaseText">
<![CDATA[To complete this step, you must know some information regarding your database server ("connection string").<br />
Please contact your ISP if necessary.
If you're installing on a local machine or server you might need information from your system administrator.]]></key>
<key alias="databaseUpgrade"><![CDATA[
If you're installing on a local machine or server you might need information from your system administrator.]]>
</key>
<key alias="databaseUpgrade">
<![CDATA[
<p>
Press the <strong>upgrade</strong> button to upgrade your database to Umbraco %0%</p>
<p>
Don't worry - no content will be deleted and everything will continue working afterwards!
</p>
]]></key>
<key alias="databaseUpgradeDone"><![CDATA[Your database has been upgraded to the final version %0%.<br />Press <strong>Next</strong> to
proceed. ]]></key>
]]>
</key>
<key alias="databaseUpgradeDone">
<![CDATA[Your database has been upgraded to the final version %0%.<br />Press <strong>Next</strong> to
proceed. ]]>
</key>
<key alias="databaseUpToDate"><![CDATA[Your current database is up-to-date!. Click <strong>next</strong> to continue the configuration wizard]]></key>
<key alias="defaultUserChangePass"><![CDATA[<strong>The Default users' password needs to be changed!</strong>]]></key>
<key alias="defaultUserDisabled"><![CDATA[<strong>The Default user has been disabled or has no access to Umbraco!</strong></p><p>No further actions needs to be taken. Click <b>Next</b> to proceed.]]></key>
<key alias="defaultUserPassChanged"><![CDATA[<strong>The Default user's password has been successfully changed since the installation!</strong></p><p>No further actions needs to be taken. Click <strong>Next</strong> to proceed.]]></key>
<key alias="defaultUserPasswordChanged">The password is changed!</key>
<key alias="defaultUserText"><![CDATA[
<key alias="defaultUserText">
<![CDATA[
<p>
Umbraco creates a default user with a login <strong>('admin')</strong> and password <strong>('default')</strong>. It's <strong>important</strong> that the password is
changed to something unique.
@@ -626,48 +647,64 @@
<p>
This step will check the default user's password and suggest if it needs to be changed.
</p>
]]></key>
]]>
</key>
<key alias="greatStart">Get a great start, watch our introduction videos</key>
<key alias="licenseText">By clicking the next button (or modifying the umbracoConfigurationStatus in web.config), you accept the license for this software as specified in the box below. Notice that this Umbraco distribution consists of two different licenses, the open source MIT license for the framework and the Umbraco freeware license that covers the UI.</key>
<key alias="None">Not installed yet.</key>
<key alias="permissionsAffectedFolders">Affected files and folders</key>
<key alias="permissionsAffectedFoldersMoreInfo">More information on setting up permissions for Umbraco here</key>
<key alias="permissionsAffectedFoldersText">You need to grant ASP.NET modify permissions to the following files/folders</key>
<key alias="permissionsAlmostPerfect"><![CDATA[<strong>Your permission settings are almost perfect!</strong><br /><br />
You can run Umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of Umbraco.]]></key>
<key alias="permissionsAlmostPerfect">
<![CDATA[<strong>Your permission settings are almost perfect!</strong><br /><br />
You can run Umbraco without problems, but you will not be able to install packages which are recommended to take full advantage of Umbraco.]]>
</key>
<key alias="permissionsHowtoResolve">How to Resolve</key>
<key alias="permissionsHowtoResolveLink">Click here to read the text version</key>
<key alias="permissionsHowtoResolveText"><![CDATA[Watch our <strong>video tutorial</strong> on setting up folder permissions for Umbraco or read the text version.]]></key>
<key alias="permissionsMaybeAnIssue"><![CDATA[<strong>Your permission settings might be an issue!</strong>
<key alias="permissionsMaybeAnIssue">
<![CDATA[<strong>Your permission settings might be an issue!</strong>
<br/><br />
You can run Umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of Umbraco.]]></key>
<key alias="permissionsNotReady"><![CDATA[<strong>Your permission settings are not ready for Umbraco!</strong>
You can run Umbraco without problems, but you will not be able to create folders or install packages which are recommended to take full advantage of Umbraco.]]>
</key>
<key alias="permissionsNotReady">
<![CDATA[<strong>Your permission settings are not ready for Umbraco!</strong>
<br /><br />
In order to run Umbraco, you'll need to update your permission settings.]]></key>
<key alias="permissionsPerfect"><![CDATA[<strong>Your permission settings are perfect!</strong><br /><br />
You are ready to run Umbraco and install packages!]]></key>
In order to run Umbraco, you'll need to update your permission settings.]]>
</key>
<key alias="permissionsPerfect">
<![CDATA[<strong>Your permission settings are perfect!</strong><br /><br />
You are ready to run Umbraco and install packages!]]>
</key>
<key alias="permissionsResolveFolderIssues">Resolving folder issue</key>
<key alias="permissionsResolveFolderIssuesLink">Follow this link for more information on problems with ASP.NET and creating folders</key>
<key alias="permissionsSettingUpPermissions">Setting up folder permissions</key>
<key alias="permissionsText"><![CDATA[
<key alias="permissionsText">
<![CDATA[
Umbraco needs write/modify access to certain directories in order to store files like pictures and PDF's.
It also stores temporary data (aka: cache) for enhancing the performance of your website.
]]></key>
]]>
</key>
<key alias="runwayFromScratch">I want to start from scratch</key>
<key alias="runwayFromScratchText"><![CDATA[
<key alias="runwayFromScratchText">
<![CDATA[
Your website is completely empty at the moment, so that's perfect if you want to start from scratch and create your own document types and templates.
(<a href="http://Umbraco.tv/documentation/videos/for-site-builders/foundation/document-types">learn how</a>)
You can still choose to install Runway later on. Please go to the Developer section and choose Packages.
]]></key>
]]>
</key>
<key alias="runwayHeader">You've just set up a clean Umbraco platform. What do you want to do next?</key>
<key alias="runwayInstalled">Runway is installed</key>
<key alias="runwayInstalledText"><![CDATA[
<key alias="runwayInstalledText">
<![CDATA[
You have the foundation in place. Select what modules you wish to install on top of it.<br />
This is our list of recommended modules, check off the ones you would like to install, or view the <a href="#" onclick="toggleModules(); return false;" id="toggleModuleList">full list of modules</a>
]]></key>
]]>
</key>
<key alias="runwayOnlyProUsers">Only recommended for experienced users</key>
<key alias="runwaySimpleSite">I want to start with a simple website</key>
<key alias="runwaySimpleSiteText"><![CDATA[
<key alias="runwaySimpleSiteText">
<![CDATA[
<p>
"Runway" is a simple website providing some basic document types and templates. The installer can set up Runway for you automatically,
but you can easily edit, extend or remove it. It's not necessary and you can perfectly use Umbraco without it. However,
@@ -678,7 +715,8 @@
<em>Included with Runway:</em> Home page, Getting Started page, Installing Modules page.<br />
<em>Optional Modules:</em> Top Navigation, Sitemap, Contact, Gallery.
</small>
]]></key>
]]>
</key>
<key alias="runwayWhatIsRunway">What is Runway</key>
<key alias="step1">Step 1/5 Accept license</key>
<key alias="step2">Step 2/5: Database configuration</key>
@@ -686,24 +724,36 @@
<key alias="step4">Step 4/5: Check Umbraco security</key>
<key alias="step5">Step 5/5: Umbraco is ready to get you started</key>
<key alias="thankYou">Thank you for choosing Umbraco</key>
<key alias="theEndBrowseSite"><![CDATA[<h3>Browse your new site</h3>
You installed Runway, so why not see how your new website looks.]]></key>
<key alias="theEndFurtherHelp"><![CDATA[<h3>Further help and information</h3>
Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the Umbraco terminology]]></key>
<key alias="theEndBrowseSite">
<![CDATA[<h3>Browse your new site</h3>
You installed Runway, so why not see how your new website looks.]]>
</key>
<key alias="theEndFurtherHelp">
<![CDATA[<h3>Further help and information</h3>
Get help from our award winning community, browse the documentation or watch some free videos on how to build a simple site, how to use packages and a quick guide to the Umbraco terminology]]>
</key>
<key alias="theEndHeader">Umbraco %0% is installed and ready for use</key>
<key alias="theEndInstallFailed"><![CDATA[To finish the installation, you'll need to
manually edit the <strong>/web.config file</strong> and update the AppSetting key <strong>UmbracoConfigurationStatus</strong> in the bottom to the value of <strong>'%0%'</strong>.]]></key>
<key alias="theEndInstallSuccess"><![CDATA[You can get <strong>started instantly</strong> by clicking the "Launch Umbraco" button below. <br />If you are <strong>new to Umbraco</strong>,
you can find plenty of resources on our getting started pages.]]></key>
<key alias="theEndOpenUmbraco"><![CDATA[<h3>Launch Umbraco</h3>
To manage your website, simply open the Umbraco back office and start adding content, updating the templates and stylesheets or add new functionality]]></key>
<key alias="theEndInstallFailed">
<![CDATA[To finish the installation, you'll need to
manually edit the <strong>/web.config file</strong> and update the AppSetting key <strong>UmbracoConfigurationStatus</strong> in the bottom to the value of <strong>'%0%'</strong>.]]>
</key>
<key alias="theEndInstallSuccess">
<![CDATA[You can get <strong>started instantly</strong> by clicking the "Launch Umbraco" button below. <br />If you are <strong>new to Umbraco</strong>,
you can find plenty of resources on our getting started pages.]]>
</key>
<key alias="theEndOpenUmbraco">
<![CDATA[<h3>Launch Umbraco</h3>
To manage your website, simply open the Umbraco back office and start adding content, updating the templates and stylesheets or add new functionality]]>
</key>
<key alias="Unavailable">Connection to database failed.</key>
<key alias="Version3">Umbraco Version 3</key>
<key alias="Version4">Umbraco Version 4</key>
<key alias="watch">Watch</key>
<key alias="welcomeIntro"><![CDATA[This wizard will guide you through the process of configuring <strong>Umbraco %0%</strong> for a fresh install or upgrading from version 3.0.
<key alias="welcomeIntro">
<![CDATA[This wizard will guide you through the process of configuring <strong>Umbraco %0%</strong> for a fresh install or upgrading from version 3.0.
<br /><br />
Press <strong>"next"</strong> to start the wizard.]]></key>
Press <strong>"next"</strong> to start the wizard.]]>
</key>
</area>
<area alias="language">
<key alias="cultureCode">Culture Code</key>
@@ -758,7 +808,8 @@ To manage your website, simply open the Umbraco back office and start adding con
</area>
<area alias="notifications">
<key alias="editNotifications">Edit your notification for %0%</key>
<key alias="mailBody"><![CDATA[
<key alias="mailBody">
<![CDATA[
Hi %0%
This is an automated mail to inform you that the task '%1%'
@@ -770,8 +821,10 @@ To manage your website, simply open the Umbraco back office and start adding con
Have a nice day!
Cheers from the Umbraco robot
]]></key>
<key alias="mailBodyHtml"><![CDATA[<p>Hi %0%</p>
]]>
</key>
<key alias="mailBodyHtml">
<![CDATA[<p>Hi %0%</p>
<p>This is an automated mail to inform you that the task <strong>'%1%'</strong>
has been performed on the page <a href="http://%4%/#/content/content/edit/%5%"><strong>'%2%'</strong></a>
@@ -797,23 +850,28 @@ To manage your website, simply open the Umbraco back office and start adding con
<p>Have a nice day!<br /><br />
Cheers from the Umbraco robot
</p>]]></key>
</p>]]>
</key>
<key alias="mailSubject">[%0%] Notification about %1% performed on %2%</key>
<key alias="notifications">Notifications</key>
</area>
<area alias="packager">
<key alias="chooseLocalPackageText"><![CDATA[
<key alias="chooseLocalPackageText">
<![CDATA[
Choose Package from your machine, by clicking the Browse<br />
button and locating the package. Umbraco packages usually have a ".zip" extension.
]]></key>
]]>
</key>
<key alias="packageAuthor">Author</key>
<key alias="packageDemonstration">Demonstration</key>
<key alias="packageDocumentation">Documentation</key>
<key alias="packageMetaData">Package meta data</key>
<key alias="packageName">Package name</key>
<key alias="packageNoItemsHeader">Package doesn't contain any items</key>
<key alias="packageNoItemsText"><![CDATA[This package file doesn't contain any items to uninstall.<br/><br/>
You can safely remove this from the system by clicking "uninstall package" below.]]></key>
<key alias="packageNoItemsText">
<![CDATA[This package file doesn't contain any items to uninstall.<br/><br/>
You can safely remove this from the system by clicking "uninstall package" below.]]>
</key>
<key alias="packageNoUpgrades">No upgrades available</key>
<key alias="packageOptions">Package options</key>
<key alias="packageReadme">Package readme</key>
@@ -822,9 +880,11 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="packageUninstalledHeader">Package was uninstalled</key>
<key alias="packageUninstalledText">The package was successfully uninstalled</key>
<key alias="packageUninstallHeader">Uninstall package</key>
<key alias="packageUninstallText"><![CDATA[You can unselect items you do not wish to remove, at this time, below. When you click "confirm uninstall" all checked-off items will be removed.<br />
<key alias="packageUninstallText">
<![CDATA[You can unselect items you do not wish to remove, at this time, below. When you click "confirm uninstall" all checked-off items will be removed.<br />
<span style="color: Red; font-weight: bold;">Notice:</span> any documents, media etc depending on the items you remove, will stop working, and could lead to system instability,
so uninstall with caution. If in doubt, contact the package author.]]></key>
so uninstall with caution. If in doubt, contact the package author.]]>
</key>
<key alias="packageUpgradeDownload">Download update from the repository</key>
<key alias="packageUpgradeHeader">Upgrade package</key>
<key alias="packageUpgradeInstructions">Upgrade instructions</key>
@@ -872,27 +932,37 @@ To manage your website, simply open the Umbraco back office and start adding con
%0% could not be published because the item is scheduled for release.
]]>
</key>
<key alias="contentPublishedFailedExpired"><![CDATA[
<key alias="contentPublishedFailedExpired">
<![CDATA[
%0% could not be published because the item has expired.
]]></key>
<key alias="contentPublishedFailedInvalid"><![CDATA[
]]>
</key>
<key alias="contentPublishedFailedInvalid">
<![CDATA[
%0% could not be published because these properties: %1% did not pass validation rules.
]]></key>
<key alias="contentPublishedFailedByEvent"><![CDATA[
]]>
</key>
<key alias="contentPublishedFailedByEvent">
<![CDATA[
%0% could not be published, a 3rd party add-in cancelled the action.
]]></key>
<key alias="contentPublishedFailedByParent"><![CDATA[
]]>
</key>
<key alias="contentPublishedFailedByParent">
<![CDATA[
%0% can not be published, because a parent page is not published.
]]></key>
]]>
</key>
<key alias="includeUnpublished">Include unpublished subpages</key>
<key alias="inProgress">Publishing in progress - please wait...</key>
<key alias="inProgressCounter">%0% out of %1% pages have been published...</key>
<key alias="nodePublish">%0% has been published</key>
<key alias="nodePublishAll">%0% and subpages have been published</key>
<key alias="publishAll">Publish %0% and all its subpages</key>
<key alias="publishHelp"><![CDATA[Click <em>Publish</em> to publish <strong>%0%</strong> and thereby making its content publicly available.<br/><br />
<key alias="publishHelp">
<![CDATA[Click <em>Publish</em> to publish <strong>%0%</strong> and thereby making its content publicly available.<br/><br />
You can publish this page and all its subpages by checking <em>Include unpublished subpages</em> below.
]]></key>
]]>
</key>
</area>
<area alias="colorpicker">
<key alias="noColors">You have not configured any approved colours</key>
@@ -973,8 +1043,8 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="sortPleaseWait"><![CDATA[ Please wait. Items are being sorted, this can take a while.<br/> <br/> Do not close this window during sorting]]></key>
</area>
<area alias="speechBubbles">
<key alias="validationFailedHeader">Validation</key>
<key alias="validationFailedMessage">Validation errors must be fixed before the item can be saved</key>
<key alias="validationFailedHeader">Validation</key>
<key alias="validationFailedMessage">Validation errors must be fixed before the item can be saved</key>
<key alias="operationFailedHeader">Failed</key>
<key alias="invalidUserPermissionsText">Insufficient user permissions, could not complete the operation</key>
<key alias="operationCancelledHeader">Cancelled</key>
@@ -1048,7 +1118,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="preview">Preview</key>
<key alias="styles">Styles</key>
</area>
<area alias="template">
<key alias="edittemplate">Edit template</key>
@@ -1058,10 +1128,10 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="insert">Insert</key>
<key alias="insertDesc">Choose what to insert into your template</key>
<key alias="insertDictionaryItem">Dictionary item</key>
<key alias="insertDictionaryItemDesc">A dictionary item is a placeholder for a translatable piece of text, which makes it easy to create designs for multilingual websites.</key>
<key alias="insertMacro">Macro</key>
<key alias="insertMacroDesc">
A Macro is a configurable component which is great for
@@ -1071,13 +1141,13 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="insertPageField">Value</key>
<key alias="insertPageFieldDesc">Displays the value of a named field from the current page, with options to modify the value or fallback to alternative values.</key>
<key alias="insertPartialView">Partial view</key>
<key alias="insertPartialViewDesc">
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.
</key>
<key alias="mastertemplate">Master template</key>
<key alias="noMastertemplate">No master template</key>
<key alias="noMaster">No master</key>
@@ -1089,8 +1159,8 @@ To manage your website, simply open the Umbraco back office and start adding con
<code>@RenderBody()</code> placeholder.
]]>
</key>
<key alias="defineSection">Define a named section</key>
<key alias="defineSectionDesc">
<![CDATA[
@@ -1113,19 +1183,19 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="sectionMandatoryDesc">
If mandatory, the child template must contain a <code>@section</code> definition, otherwise an error is shown.
</key>
<key alias="queryBuilder">Query builder</key>
<key alias="itemsReturned">items returned, in</key>
<key alias="iWant">I want</key>
<key alias="iWant">I want</key>
<key alias="allContent">all content</key>
<key alias="contentOfType">content of type &quot;%0%&quot;</key>
<key alias="from">from</key>
<key alias="websiteRoot">my website</key>
<key alias="where">where</key>
<key alias="and">and</key>
<key alias="is">is</key>
<key alias="isNot">is not</key>
<key alias="before">before</key>
@@ -1148,12 +1218,12 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="orderBy">order by</key>
<key alias="ascending">ascending</key>
<key alias="descending">descending</key>
<key alias="descending">descending</key>
<key alias="template">Template</key>
</area>
<area alias="grid">
<key alias="insertControl">Choose type of content</key>
<key alias="chooseLayout">Choose a layout</key>
@@ -1200,63 +1270,63 @@ To manage your website, simply open the Umbraco back office and start adding con
<area alias="contentTypeEditor">
<key alias="compositions">Compositions</key>
<key alias="noTabs">You have not added any tabs</key>
<key alias="addNewTab">Add new tab</key>
<key alias="addAnotherTab">Add another tab</key>
<key alias="inheritedFrom">Inherited from</key>
<key alias="addProperty">Add property</key>
<key alias="requiredLabel">Required label</key>
<key alias="compositions">Compositions</key>
<key alias="noTabs">You have not added any tabs</key>
<key alias="addNewTab">Add new tab</key>
<key alias="addAnotherTab">Add another tab</key>
<key alias="inheritedFrom">Inherited from</key>
<key alias="addProperty">Add property</key>
<key alias="requiredLabel">Required label</key>
<key alias="enableListViewHeading">Enable list view</key>
<key alias="enableListViewDescription">Configures the content item to show a sortable and searchable list of its children, the children will not be shown in the tree</key>
<key alias="enableListViewHeading">Enable list view</key>
<key alias="enableListViewDescription">Configures the content item to show a sortable and searchable list of its children, the children will not be shown in the tree</key>
<key alias="allowedTemplatesHeading">Allowed Templates</key>
<key alias="allowedTemplatesDescription">Choose which templates editors are allowed to use on content of this type</key>
<key alias="allowedTemplatesHeading">Allowed Templates</key>
<key alias="allowedTemplatesDescription">Choose which templates editors are allowed to use on content of this type</key>
<key alias="allowAsRootHeading">Allow as root</key>
<key alias="allowAsRootDescription">Allow editors to create content of this type in the root of the content tree</key>
<key alias="allowAsRootCheckbox">Yes - allow content of this type in the root</key>
<key alias="allowAsRootHeading">Allow as root</key>
<key alias="allowAsRootDescription">Allow editors to create content of this type in the root of the content tree</key>
<key alias="allowAsRootCheckbox">Yes - allow content of this type in the root</key>
<key alias="childNodesHeading">Allowed child node types</key>
<key alias="childNodesDescription">Allow content of the specified types to be created underneath content of this type</key>
<key alias="childNodesHeading">Allowed child node types</key>
<key alias="childNodesDescription">Allow content of the specified types to be created underneath content of this type</key>
<key alias="chooseChildNode">Choose child node</key>
<key alias="chooseChildNode">Choose child node</key>
<key alias="compositionsDescription">Inherit tabs and properties from an existing document type. New tabs will be added to the current document type or merged if a tab with an identical name exists.</key>
<key alias="compositionInUse">This content type is used in a composition, and therefore cannot be composed itself.</key>
<key alias="noAvailableCompositions">There are no content types available to use as a composition.</key>
<key alias="compositionsDescription">Inherit tabs and properties from an existing document type. New tabs will be added to the current document type or merged if a tab with an identical name exists.</key>
<key alias="compositionInUse">This content type is used in a composition, and therefore cannot be composed itself.</key>
<key alias="noAvailableCompositions">There are no content types available to use as a composition.</key>
<key alias="availableEditors">Available editors</key>
<key alias="reuse">Reuse</key>
<key alias="editorSettings">Editor settings</key>
<key alias="availableEditors">Available editors</key>
<key alias="reuse">Reuse</key>
<key alias="editorSettings">Editor settings</key>
<key alias="configuration">Configuration</key>
<key alias="configuration">Configuration</key>
<key alias="yesDelete">Yes, delete</key>
<key alias="yesDelete">Yes, delete</key>
<key alias="movedUnderneath">was moved underneath</key>
<key alias="copiedUnderneath">was copied underneath</key>
<key alias="folderToMove">Select the folder to move</key>
<key alias="folderToCopy">Select the folder to copy</key>
<key alias="structureBelow">to in the tree structure below</key>
<key alias="movedUnderneath">was moved underneath</key>
<key alias="copiedUnderneath">was copied underneath</key>
<key alias="folderToMove">Select the folder to move</key>
<key alias="folderToCopy">Select the folder to copy</key>
<key alias="structureBelow">to in the tree structure below</key>
<key alias="allDocumentTypes">All Document types</key>
<key alias="allDocuments">All Documents</key>
<key alias="allMediaItems">All media items</key>
<key alias="allDocumentTypes">All Document types</key>
<key alias="allDocuments">All Documents</key>
<key alias="allMediaItems">All media items</key>
<key alias="usingThisDocument">using this document type will be deleted permanently, please confirm you want to delete these as well.</key>
<key alias="usingThisMedia">using this media type will be deleted permanently, please confirm you want to delete these as well.</key>
<key alias="usingThisMember">using this member type will be deleted permanently, please confirm you want to delete these as well</key>
<key alias="usingThisDocument">using this document type will be deleted permanently, please confirm you want to delete these as well.</key>
<key alias="usingThisMedia">using this media type will be deleted permanently, please confirm you want to delete these as well.</key>
<key alias="usingThisMember">using this member type will be deleted permanently, please confirm you want to delete these as well</key>
<key alias="andAllDocuments">and all documents using this type</key>
<key alias="andAllMediaItems">and all media items using this type</key>
<key alias="andAllMembers">and all members using this type</key>
<key alias="andAllDocuments">and all documents using this type</key>
<key alias="andAllMediaItems">and all media items using this type</key>
<key alias="andAllMembers">and all members using this type</key>
<key alias="thisEditorUpdateSettings">using this editor will get updated with the new settings</key>
<key alias="thisEditorUpdateSettings">using this editor will get updated with the new settings</key>
<key alias="memberCanEdit">Member can edit</key>
<key alias="showOnMemberProfile">Show on member profile</key>
<key alias="memberCanEdit">Member can edit</key>
<key alias="showOnMemberProfile">Show on member profile</key>
</area>
@@ -1292,10 +1362,12 @@ To manage your website, simply open the Umbraco back office and start adding con
</area>
<area alias="translation">
<key alias="assignedTasks">Tasks assigned to you</key>
<key alias="assignedTasksHelp"><![CDATA[ The list below shows translation tasks <strong>assigned to you</strong>. To see a detailed view including comments, click on "Details" or just the page name.
<key alias="assignedTasksHelp">
<![CDATA[ The list below shows translation tasks <strong>assigned to you</strong>. To see a detailed view including comments, click on "Details" or just the page name.
You can also download the page as XML directly by clicking the "Download Xml" link. <br/>
To close a translation task, please go to the Details view and click the "Close" button.
]]></key>
]]>
</key>
<key alias="closeTask">close task</key>
<key alias="details">Translation details</key>
<key alias="downloadAllAsXml">Download all translation tasks as XML</key>
@@ -1303,7 +1375,8 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="DownloadXmlDTD">Download XML DTD</key>
<key alias="fields">Fields</key>
<key alias="includeSubpages">Include subpages</key>
<key alias="mailBody"><![CDATA[
<key alias="mailBody">
<![CDATA[
Hi %0%
This is an automated mail to inform you that the document '%1%'
@@ -1317,14 +1390,17 @@ To manage your website, simply open the Umbraco back office and start adding con
Have a nice day!
Cheers from the Umbraco robot
]]></key>
]]>
</key>
<key alias="mailSubject">[%0%] Translation task for %1%</key>
<key alias="noTranslators">No translator users found. Please create a translator user before you start sending content to translation</key>
<key alias="ownedTasks">Tasks created by you</key>
<key alias="ownedTasksHelp"><![CDATA[ The list below shows pages <strong>created by you</strong>. To see a detailed view including comments,
<key alias="ownedTasksHelp">
<![CDATA[ The list below shows pages <strong>created by you</strong>. To see a detailed view including comments,
click on "Details" or just the page name. You can also download the page as XML directly by clicking the "Download Xml" link.
To close a translation task, please go to the Details view and click the "Close" button.
]]></key>
]]>
</key>
<key alias="pageHasBeenSendToTranslation">The page '%0%' has been send to translation</key>
<key alias="noLanguageSelected">Please select the language that the content should be translated into</key>
<key alias="sendToTranslate">Send the page '%0%' to translation</key>
@@ -1340,6 +1416,8 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="uploadTranslationXml">Upload translation XML</key>
</area>
<area alias="treeHeaders">
<key alias="content">Content</key>
<key alias="media">Media</key>
<key alias="cacheBrowser">Cache Browser</key>
<key alias="contentRecycleBin">Recycle Bin</key>
<key alias="createdPackages">Created packages</key>
@@ -1370,7 +1448,7 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="templates">Templates</key>
<key alias="xslt">XSLT Files</key>
<key alias="analytics">Analytics</key>
<key alias="partialViews">Partial Views</key>
<key alias="partialViews">Partial Views</key>
<key alias="partialViewMacros">Partial View Macro Files</key>
</area>
<area alias="update">
@@ -1425,80 +1503,80 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="sessionExpires" version="7.0">Session expires in</key>
</area>
<area alias="validation">
<key alias="validation">Validation</key>
<key alias="validateAsEmail">Validate as email</key>
<key alias="validateAsNumber">Validate as a number</key>
<key alias="validateAsUrl">Validate as a Url</key>
<key alias="enterCustomValidation">...or enter a custom validation</key>
<key alias="fieldIsMandatory">Field is mandatory</key>
<key alias="validation">Validation</key>
<key alias="validateAsEmail">Validate as email</key>
<key alias="validateAsNumber">Validate as a number</key>
<key alias="validateAsUrl">Validate as a Url</key>
<key alias="enterCustomValidation">...or enter a custom validation</key>
<key alias="fieldIsMandatory">Field is mandatory</key>
</area>
<area alias="healthcheck">
<!-- The following keys get these tokens passed in:
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
2: XPath
3: Configuration file path
-->
<key alias="checkSuccessMessage">Value is set to the recommended value: '%0%'.</key>
<key alias="rectifySuccessMessage">Value was set to '%1%' for XPath '%2%' in configuration file '%3%'.</key>
<key alias="rectifySuccessMessage">Value was set to '%1%' for XPath '%2%' in configuration file '%3%'.</key>
<key alias="checkErrorMessageDifferentExpectedValue">Expected value '%1%' for '%2%' in configuration file '%3%', but found '%0%'.</key>
<key alias="checkErrorMessageUnexpectedValue">Found unexpected value '%0%' for '%2%' in configuration file '%3%'.</key>
<!-- The following keys get these tokens passed in:
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
-->
<key alias="customErrorsCheckSuccessMessage">Custom errors are set to '%0%'.</key>
<key alias="customErrorsCheckErrorMessage">Custom errors are currently set to '%0%'. It is recommended to set this to '%1%' before go live.</key>
<key alias="customErrorsCheckRectifySuccessMessage">Custom errors successfully set to '%0%'.</key>
<key alias="customErrorsCheckSuccessMessage">Custom errors are set to '%0%'.</key>
<key alias="customErrorsCheckErrorMessage">Custom errors are currently set to '%0%'. It is recommended to set this to '%1%' before go live.</key>
<key alias="customErrorsCheckRectifySuccessMessage">Custom errors successfully set to '%0%'.</key>
<key alias="macroErrorModeCheckSuccessMessage">MacroErrors are set to '%0%'.</key>
<key alias="macroErrorModeCheckErrorMessage">MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely if there are any errors in macros. Rectifying this will set the value to '%1%'.</key>
<key alias="macroErrorModeCheckRectifySuccessMessage">MacroErrors are now set to '%0%'.</key>
<key alias="macroErrorModeCheckSuccessMessage">MacroErrors are set to '%0%'.</key>
<key alias="macroErrorModeCheckErrorMessage">MacroErrors are set to '%0%' which will prevent some or all pages in your site from loading completely if there are any errors in macros. Rectifying this will set the value to '%1%'.</key>
<key alias="macroErrorModeCheckRectifySuccessMessage">MacroErrors are now set to '%0%'.</key>
<!-- The following keys get these tokens passed in:
<!-- The following keys get these tokens passed in:
0: Current value
1: Recommended value
2: Server version
-->
<key alias="trySkipIisCustomErrorsCheckSuccessMessage">Try Skip IIS Custom Errors is set to '%0%' and you're using IIS version '%1%'.</key>
<key alias="trySkipIisCustomErrorsCheckErrorMessage">Try Skip IIS Custom Errors is currently '%0%'. It is recommended to set this to '%1%' for your IIS version (%2%).</key>
<key alias="trySkipIisCustomErrorsCheckRectifySuccessMessage">Try Skip IIS Custom Errors successfully set to '%0%'.</key>
<key alias="trySkipIisCustomErrorsCheckSuccessMessage">Try Skip IIS Custom Errors is set to '%0%' and you're using IIS version '%1%'.</key>
<key alias="trySkipIisCustomErrorsCheckErrorMessage">Try Skip IIS Custom Errors is currently '%0%'. It is recommended to set this to '%1%' for your IIS version (%2%).</key>
<key alias="trySkipIisCustomErrorsCheckRectifySuccessMessage">Try Skip IIS Custom Errors successfully set to '%0%'.</key>
<!-- The following keys get predefined tokens passed in that are not all the same, like above -->
<key alias="configurationServiceFileNotFound">File does not exist: '%0%'.</key>
<key alias="configurationServiceNodeNotFound"><![CDATA[Unable to find <strong>'%0%'</strong> in config file <strong>'%1%'</strong>.]]></key>
<key alias="configurationServiceError">There was an error, check log for full error: %0%.</key>
<!-- The following keys get predefined tokens passed in that are not all the same, like above -->
<key alias="configurationServiceFileNotFound">File does not exist: '%0%'.</key>
<key alias="configurationServiceNodeNotFound"><![CDATA[Unable to find <strong>'%0%'</strong> in config file <strong>'%1%'</strong>.]]></key>
<key alias="configurationServiceError">There was an error, check log for full error: %0%.</key>
<key alias="xmlDataIntegrityCheckMembers">Members - Total XML: %0%, Total: %1%, Total invalid: %2%</key>
<key alias="xmlDataIntegrityCheckMedia">Media - Total XML: %0%, Total: %1%, Total invalid: %2%</key>
<key alias="xmlDataIntegrityCheckContent">Content - Total XML: %0%, Total published: %1%, Total invalid: %2%</key>
<key alias="xmlDataIntegrityCheckMembers">Members - Total XML: %0%, Total: %1%, Total invalid: %2%</key>
<key alias="xmlDataIntegrityCheckMedia">Media - Total XML: %0%, Total: %1%, Total invalid: %2%</key>
<key alias="xmlDataIntegrityCheckContent">Content - Total XML: %0%, Total published: %1%, Total invalid: %2%</key>
<key alias="httpsCheckValidCertificate">Your site certificate was marked as valid.</key>
<key alias="httpsCheckInvalidCertificate">Certificate validation error: '%0%'</key>
<key alias="httpsCheckInvalidUrl">Error pinging the URL %0% - '%1%'</key>
<key alias="httpsCheckIsCurrentSchemeHttps">You are currently %0% viewing the site using the HTTPS scheme.</key>
<key alias="httpsCheckConfigurationRectifyNotPossible">The appSetting 'umbracoUseSSL' is set to 'false' in your web.config file. Once you access this site using the HTTPS scheme, that should be set to 'true'.</key>
<key alias="httpsCheckConfigurationCheckResult">The appSetting 'umbracoUseSSL' is set to '%0%' in your web.config file, your cookies are %1% marked as secure.</key>
<key alias="httpsCheckEnableHttpsError">Could not update the 'umbracoUseSSL' setting in your web.config file. Error: %0%</key>
<key alias="httpsCheckValidCertificate">Your site certificate was marked as valid.</key>
<key alias="httpsCheckInvalidCertificate">Certificate validation error: '%0%'</key>
<key alias="httpsCheckInvalidUrl">Error pinging the URL %0% - '%1%'</key>
<key alias="httpsCheckIsCurrentSchemeHttps">You are currently %0% viewing the site using the HTTPS scheme.</key>
<key alias="httpsCheckConfigurationRectifyNotPossible">The appSetting 'umbracoUseSSL' is set to 'false' in your web.config file. Once you access this site using the HTTPS scheme, that should be set to 'true'.</key>
<key alias="httpsCheckConfigurationCheckResult">The appSetting 'umbracoUseSSL' is set to '%0%' in your web.config file, your cookies are %1% marked as secure.</key>
<key alias="httpsCheckEnableHttpsError">Could not update the 'umbracoUseSSL' setting in your web.config file. Error: %0%</key>
<!-- The following keys don't get tokens passed in -->
<key alias="httpsCheckEnableHttpsButton">Enable HTTPS</key>
<key alias="httpsCheckEnableHttpsDescription">Sets umbracoSSL setting to true in the appSettings of the web.config file.</key>
<key alias="httpsCheckEnableHttpsSuccess">The appSetting 'umbracoUseSSL' is now set to 'true' in your web.config file, your cookies will be marked as secure.</key>
<!-- The following keys don't get tokens passed in -->
<key alias="httpsCheckEnableHttpsButton">Enable HTTPS</key>
<key alias="httpsCheckEnableHttpsDescription">Sets umbracoSSL setting to true in the appSettings of the web.config file.</key>
<key alias="httpsCheckEnableHttpsSuccess">The appSetting 'umbracoUseSSL' is now set to 'true' in your web.config file, your cookies will be marked as secure.</key>
<key alias="rectifyButton">Fix</key>
<key alias="rectifyButton">Fix</key>
<key alias="cannotRectifyShouldNotEqual">Cannot fix a check with a value comparison type of 'ShouldNotEqual'.</key>
<key alias="cannotRectifyShouldEqualWithValue">Cannot fix a check with a value comparison type of 'ShouldEqual' with a provided value.</key>
<key alias="valueToRectifyNotProvided">Value to fix check not provided.</key>
<key alias="compilationDebugCheckSuccessMessage">Debug compilation mode is disabled.</key>
<key alias="compilationDebugCheckErrorMessage">Debug compilation mode is currently enabled. It is recommended to disable this setting before go live.</key>
<key alias="compilationDebugCheckRectifySuccessMessage">Debug compilation mode successfully disabled.</key>
<key alias="compilationDebugCheckSuccessMessage">Debug compilation mode is disabled.</key>
<key alias="compilationDebugCheckErrorMessage">Debug compilation mode is currently enabled. It is recommended to disable this setting before go live.</key>
<key alias="compilationDebugCheckRectifySuccessMessage">Debug compilation mode successfully disabled.</key>
<key alias="traceModeCheckSuccessMessage">Trace mode is disabled.</key>
<key alias="traceModeCheckErrorMessage">Trace mode is currently enabled. It is recommended to disable this setting before go live.</key>
<key alias="traceModeCheckRectifySuccessMessage">Trace mode successfully disabled.</key>
<key alias="traceModeCheckSuccessMessage">Trace mode is disabled.</key>
<key alias="traceModeCheckErrorMessage">Trace mode is currently enabled. It is recommended to disable this setting before go live.</key>
<key alias="traceModeCheckRectifySuccessMessage">Trace mode successfully disabled.</key>
<key alias="folderPermissionsCheckMessage">All folders have the correct permissions set.</key>
<!-- The following keys get these tokens passed in:

View File

@@ -1337,6 +1337,8 @@ To manage your website, simply open the Umbraco back office and start adding con
<key alias="uploadTranslationXml">Upload translation XML</key>
</area>
<area alias="treeHeaders">
<key alias="content">Content</key>
<key alias="media">Media</key>
<key alias="cacheBrowser">Cache Browser</key>
<key alias="contentRecycleBin">Recycle Bin</key>
<key alias="createdPackages">Created packages</key>

View File

@@ -21,6 +21,7 @@ using Umbraco.Core.Persistence.DatabaseModelDefinitions;
using System.Web.Http.Controllers;
using Umbraco.Core.Xml;
using Umbraco.Web.Search;
using Umbraco.Web.Trees;
namespace Umbraco.Web.Editors
{
@@ -91,8 +92,8 @@ namespace Umbraco.Web.Editors
return Enumerable.Empty<EntityBasic>();
return ExamineSearch(query, type, searchFrom);
}
}
/// <summary>
/// Searches for all content that the user is allowed to see (based on their allowed sections)
/// </summary>
@@ -105,51 +106,40 @@ namespace Umbraco.Web.Editors
///
/// The reason a user is allowed to search individual entity types that they are not allowed to edit is because those search
/// methods might be used in things like pickers in the content editor.
/// </remarks>
/// </remarks>
[HttpGet]
public IEnumerable<TreeSearchResult> SearchAll(string query)
public IDictionary<string, TreeSearchResult> SearchAll(string query)
{
var result = new Dictionary<string, TreeSearchResult>();
if (string.IsNullOrEmpty(query))
return Enumerable.Empty<TreeSearchResult>();
var result = new List<TreeSearchResult>();
return result;
var allowedSections = Security.CurrentUser.AllowedSections.ToArray();
var searchableTrees = SearchableTreeResolver.Current.SearchableTrees;
foreach (var searchableTree in searchableTrees)
{
if (allowedSections.Contains(searchableTree.AppAlias))
{
var attribute = searchableTree.SearchableTree.GetType().GetCustomAttribute<SearchableTreeAttribute>(false);
var tree = Services.ApplicationTreeService.GetByAlias(searchableTree.TreeAlias);
if (tree == null) continue; //shouldn't occur
var searchableTreeAttribute = searchableTree.SearchableTree.GetType().GetCustomAttribute<SearchableTreeAttribute>(false);
var treeAttribute = tree.GetTreeAttribute();
int total;
result.Add(new TreeSearchResult
{
long total;
result[treeAttribute.GetRootNodeDisplayName(Services.TextService)] = new TreeSearchResult
{
Results = searchableTree.SearchableTree.Search(query, 200, 0, out total),
TreeAlias = searchableTree.TreeAlias,
AppAlias = searchableTree.AppAlias,
JsFormatterService = attribute == null ? "" : attribute.ServiceName,
JsFormatterMethod = attribute == null ? "" : attribute.MethodName
});
TreeAlias = searchableTree.TreeAlias,
AppAlias = searchableTree.AppAlias,
JsFormatterService = searchableTreeAttribute == null ? "" : searchableTreeAttribute.ServiceName,
JsFormatterMethod = searchableTreeAttribute == null ? "" : searchableTreeAttribute.MethodName
};
}
}
//if (allowedSections.InvariantContains(Constants.Applications.Media))
//{
// result.Add(new TreeSearchResult
// {
// Results = ExamineSearch(query, UmbracoEntityTypes.Media),
// TreeTypeAlias = UmbracoEntityTypes.Media.ToString()
// });
//}
//if (allowedSections.InvariantContains(Constants.Applications.Members))
//{
// result.Add(new TreeSearchResult
// {
// Results = ExamineSearch(query, UmbracoEntityTypes.Member),
// TreeTypeAlias = UmbracoEntityTypes.Member.ToString()
// });
//}
}
return result;
}
@@ -469,7 +459,7 @@ namespace Umbraco.Web.Editors
//the EntityService cannot search members of a certain type, this is currently not supported and would require
//quite a bit of plumbing to do in the Services/Repository, we'll revert to a paged search
int total;
long total;
var searchResult = _treeSearcher.ExamineSearch(Umbraco, filter ?? "", type, pageSize, pageNumber - 1, out total, id);
return new PagedResult<EntityBasic>(total, pageNumber, pageSize)
@@ -605,7 +595,7 @@ namespace Umbraco.Web.Editors
/// <returns></returns>
private IEnumerable<SearchResultItem> ExamineSearch(string query, UmbracoEntityTypes entityType, string searchFrom = null)
{
int total;
long total;
return _treeSearcher.ExamineSearch(Umbraco, query, entityType, 200, 0, out total, searchFrom);
}

View File

@@ -10,6 +10,12 @@ namespace Umbraco.Web.Models.ContentEditing
/// </summary>
[DataMember(Name = "score")]
public float Score { get; set; }
//TODO: Enable this!
///// <summary>
///// A caption for the search result
///// </summary>
//[DataMember(Name = "caption")]
//public string Caption { get; set; }
}
}

View File

@@ -6,6 +6,7 @@ using Examine;
using Examine.LuceneEngine.Providers;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Models.Mapping;
using Umbraco.Core.Models.Membership;
using Umbraco.Web.Models.ContentEditing;
@@ -28,7 +29,7 @@ namespace Umbraco.Web.Models.Mapping
{
basic.Icon = "icon-user";
}
});
});
config.CreateMap<PropertyType, EntityBasic>()
.ForMember(x => x.Udi, expression => expression.Ignore())
@@ -64,20 +65,7 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(basic => basic.ParentId, expression => expression.UseValue(-1))
.ForMember(dto => dto.Trashed, expression => expression.Ignore())
.ForMember(x => x.AdditionalData, expression => expression.Ignore());
//config.CreateMap<EntityBasic, ITemplate>()
// .ConstructUsing(basic => new Template(basic.Name, basic.Alias)
// {
// Id = Convert.ToInt32(basic.Id),
// Key = basic.Key
// })
// .ForMember(t => t.Path, expression => expression.Ignore())
// .ForMember(t => t.Id, expression => expression.MapFrom(template => Convert.ToInt32(template.Id)))
// .ForMember(x => x.VirtualPath, expression => expression.Ignore())
// .ForMember(x => x.CreateDate, expression => expression.Ignore())
// .ForMember(x => x.UpdateDate, expression => expression.Ignore())
// .ForMember(x => x.Content, expression => expression.Ignore());
config.CreateMap<EntityBasic, ContentTypeSort>()
.ForMember(x => x.Id, expression => expression.MapFrom(entity => new Lazy<int>(() => Convert.ToInt32(entity.Id))))
.ForMember(x => x.SortOrder, expression => expression.Ignore());
@@ -89,6 +77,26 @@ namespace Umbraco.Web.Models.Mapping
.ForMember(dto => dto.Trashed, expression => expression.Ignore())
.ForMember(x => x.AdditionalData, expression => expression.Ignore());
config.CreateMap<UmbracoEntity, SearchResultItem>()
.ForMember(x => x.Udi, expression => expression.MapFrom(x => Udi.Create(UmbracoObjectTypesExtensions.GetUdiType(x.NodeObjectTypeId), x.Key)))
.ForMember(basic => basic.Icon, expression => expression.MapFrom(entity => entity.ContentTypeIcon))
.ForMember(dto => dto.Trashed, expression => expression.Ignore())
.ForMember(x => x.Alias, expression => expression.Ignore())
.AfterMap((entity, basic) =>
{
if (basic.Icon.IsNullOrWhiteSpace())
{
if (entity.NodeObjectTypeId == Constants.ObjectTypes.MemberGuid)
basic.Icon = "icon-user";
else if (entity.NodeObjectTypeId == Constants.ObjectTypes.DataTypeGuid)
basic.Icon = "icon-autofill";
else if (entity.NodeObjectTypeId == Constants.ObjectTypes.DocumentTypeGuid)
basic.Icon = "icon-item-arrangement";
else if (entity.NodeObjectTypeId == Constants.ObjectTypes.MediaTypeGuid)
basic.Icon = "icon-thumbnails";
}
});
config.CreateMap<SearchResult, SearchResultItem>()
//default to document icon
.ForMember(x => x.Score, expression => expression.MapFrom(result => result.Score))

View File

@@ -0,0 +1,21 @@
using System.Collections.Generic;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Search
{
public interface ISearchableTree
{
/// <summary>
/// Searches for results based on the entity type
/// </summary>
/// <param name="query"></param>
/// <param name="totalFound"></param>
/// <param name="searchFrom">
/// A starting point for the search, generally a node id, but for members this is a member type alias
/// </param>
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <returns></returns>
IEnumerable<SearchResultItem> Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null);
}
}

View File

@@ -0,0 +1,17 @@
namespace Umbraco.Web.Search
{
public class SearchableApplicationTree
{
public SearchableApplicationTree(string appAlias, string treeAlias, ISearchableTree searchableTree)
{
AppAlias = appAlias;
TreeAlias = treeAlias;
SearchableTree = searchableTree;
}
public string AppAlias { get; private set; }
public string TreeAlias { get; private set; }
public ISearchableTree SearchableTree { get; private set; }
}
}

View File

@@ -0,0 +1,35 @@
using System;
namespace Umbraco.Web.Search
{
[AttributeUsage(AttributeTargets.Class)]
public sealed class SearchableTreeAttribute : Attribute
{
/// <summary>
/// This constructor defines both the angular service and method name to use
/// </summary>
/// <param name="serviceName"></param>
/// <param name="methodName"></param>
public SearchableTreeAttribute(string serviceName, string methodName)
{
if (string.IsNullOrWhiteSpace(serviceName)) throw new ArgumentException("Value cannot be null or whitespace.", "serviceName");
if (string.IsNullOrWhiteSpace(methodName)) throw new ArgumentException("Value cannot be null or whitespace.", "methodName");
MethodName = methodName;
ServiceName = serviceName;
}
/// <summary>
/// This constructor will assume that the method name equals `format(searchResult, appAlias, treeAlias)`
/// </summary>
/// <param name="serviceName"></param>
public SearchableTreeAttribute(string serviceName)
{
if (string.IsNullOrWhiteSpace(serviceName)) throw new ArgumentException("Value cannot be null or whitespace.", "serviceName");
MethodName = "";
ServiceName = serviceName;
}
public string MethodName { get; private set; }
public string ServiceName { get; private set; }
}
}

View File

@@ -0,0 +1,12 @@
using System.Collections.ObjectModel;
namespace Umbraco.Web.Search
{
public class SearchableTreeCollection : KeyedCollection<string, SearchableApplicationTree>
{
protected override string GetKeyForItem(SearchableApplicationTree item)
{
return item.TreeAlias;
}
}
}

View File

@@ -0,0 +1,60 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using Umbraco.Core;
using Umbraco.Core.Logging;
using Umbraco.Core.Models;
using Umbraco.Core.ObjectResolution;
using Umbraco.Core.Services;
using Umbraco.Web.Editors;
using Umbraco.Web.Trees;
namespace Umbraco.Web.Search
{
public class SearchableTreeResolver : LazyManyObjectsResolverBase<SearchableTreeResolver, ISearchableTree>
{
private readonly IApplicationTreeService _treeService;
public SearchableTreeResolver(IServiceProvider serviceProvider, ILogger logger, IApplicationTreeService treeService, Func<IEnumerable<Type>> searchableTrees)
: base(serviceProvider, logger, searchableTrees, ObjectLifetimeScope.Application)
{
_treeService = treeService;
}
private SearchableTreeCollection _resolved;
private static readonly object Locker = new object();
/// <summary>
/// Returns the a dictionary of tree alias with it's affiliated <see cref="ISearchableTree"/>
/// </summary>
public SearchableTreeCollection SearchableTrees
{
get
{
if (_resolved != null) return _resolved;
lock (Locker)
{
var appTrees = _treeService.GetAll().ToArray();
_resolved = new SearchableTreeCollection();
var searchableTrees = Values.ToArray();
foreach (var instanceType in InstanceTypes)
{
if (TypeHelper.IsTypeAssignableFrom<ISearchableTree>(instanceType))
{
var found = appTrees.FirstOrDefault(x => x.GetRuntimeType() == instanceType);
if (found != null)
{
_resolved.Add(new SearchableApplicationTree(found.ApplicationAlias, found.Alias, searchableTrees.First(x => x.GetType() == instanceType)));
}
}
}
return _resolved;
}
}
}
}
}

View File

@@ -0,0 +1,299 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using AutoMapper;
using Examine;
using Umbraco.Core;
using Umbraco.Web.Models.ContentEditing;
namespace Umbraco.Web.Search
{
internal class UmbracoTreeSearcher
{
/// <summary>
/// Searches for results based on the entity type
/// </summary>
/// <param name="umbracoHelper"></param>
/// <param name="query"></param>
/// <param name="entityType"></param>
/// <param name="totalFound"></param>
/// <param name="searchFrom">
/// A starting point for the search, generally a node id, but for members this is a member type alias
/// </param>
/// <param name="pageSize"></param>
/// <param name="pageIndex"></param>
/// <returns></returns>
public IEnumerable<SearchResultItem> ExamineSearch(
UmbracoHelper umbracoHelper,
string query,
UmbracoEntityTypes entityType,
int pageSize,
long pageIndex, out long totalFound, string searchFrom = null)
{
var sb = new StringBuilder();
string type;
var searcher = Constants.Examine.InternalSearcher;
var fields = new[] { "id", "__NodeId" };
//TODO: WE should really just allow passing in a lucene raw query
switch (entityType)
{
case UmbracoEntityTypes.Member:
searcher = Constants.Examine.InternalMemberSearcher;
type = "member";
fields = new[] { "id", "__NodeId", "email", "loginName" };
if (searchFrom != null && searchFrom != Constants.Conventions.MemberTypes.AllMembersListId && searchFrom.Trim() != "-1")
{
sb.Append("+__NodeTypeAlias:");
sb.Append(searchFrom);
sb.Append(" ");
}
break;
case UmbracoEntityTypes.Media:
type = "media";
var mediaSearchFrom = int.MinValue;
if (umbracoHelper.UmbracoContext.Security.CurrentUser.StartMediaId > 0 ||
//if searchFrom is specified and it is greater than 0
(searchFrom != null && int.TryParse(searchFrom, out mediaSearchFrom) && mediaSearchFrom > 0))
{
sb.Append("+__Path: \\-1*\\,");
sb.Append(mediaSearchFrom > 0
? mediaSearchFrom.ToString(CultureInfo.InvariantCulture)
: umbracoHelper.UmbracoContext.Security.CurrentUser.StartMediaId.ToString(CultureInfo.InvariantCulture));
sb.Append("\\,* ");
}
break;
case UmbracoEntityTypes.Document:
type = "content";
var contentSearchFrom = int.MinValue;
if (umbracoHelper.UmbracoContext.Security.CurrentUser.StartContentId > 0 ||
//if searchFrom is specified and it is greater than 0
(searchFrom != null && int.TryParse(searchFrom, out contentSearchFrom) && contentSearchFrom > 0))
{
sb.Append("+__Path: \\-1*\\,");
sb.Append(contentSearchFrom > 0
? contentSearchFrom.ToString(CultureInfo.InvariantCulture)
: umbracoHelper.UmbracoContext.Security.CurrentUser.StartContentId.ToString(CultureInfo.InvariantCulture));
sb.Append("\\,* ");
}
break;
default:
throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType);
}
var internalSearcher = ExamineManager.Instance.SearchProviderCollection[searcher];
//build a lucene query:
// the __nodeName will be boosted 10x without wildcards
// then __nodeName will be matched normally with wildcards
// the rest will be normal without wildcards
//check if text is surrounded by single or double quotes, if so, then exact match
var surroundedByQuotes = Regex.IsMatch(query, "^\".*?\"$")
|| Regex.IsMatch(query, "^\'.*?\'$");
if (surroundedByQuotes)
{
//strip quotes, escape string, the replace again
query = query.Trim(new[] { '\"', '\'' });
query = Lucene.Net.QueryParsers.QueryParser.Escape(query);
//nothing to search
if (searchFrom.IsNullOrWhiteSpace() && query.IsNullOrWhiteSpace())
{
totalFound = 0;
return new List<SearchResultItem>();
}
//update the query with the query term
if (query.IsNullOrWhiteSpace() == false)
{
//add back the surrounding quotes
query = string.Format("{0}{1}{0}", "\"", query);
//node name exactly boost x 10
sb.Append("+(__nodeName: (");
sb.Append(query.ToLower());
sb.Append(")^10.0 ");
foreach (var f in fields)
{
//additional fields normally
sb.Append(f);
sb.Append(": (");
sb.Append(query);
sb.Append(") ");
}
sb.Append(") ");
}
}
else
{
var trimmed = query.Trim(new[] { '\"', '\'' });
//nothing to search
if (searchFrom.IsNullOrWhiteSpace() && trimmed.IsNullOrWhiteSpace())
{
totalFound = 0;
return new List<SearchResultItem>();
}
//update the query with the query term
if (trimmed.IsNullOrWhiteSpace() == false)
{
query = Lucene.Net.QueryParsers.QueryParser.Escape(query);
var querywords = query.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
//node name exactly boost x 10
sb.Append("+(__nodeName:");
sb.Append("\"");
sb.Append(query.ToLower());
sb.Append("\"");
sb.Append("^10.0 ");
//node name normally with wildcards
sb.Append(" __nodeName:");
sb.Append("(");
foreach (var w in querywords)
{
sb.Append(w.ToLower());
sb.Append("* ");
}
sb.Append(") ");
foreach (var f in fields)
{
//additional fields normally
sb.Append(f);
sb.Append(":");
sb.Append("(");
foreach (var w in querywords)
{
sb.Append(w.ToLower());
sb.Append("* ");
}
sb.Append(")");
sb.Append(" ");
}
sb.Append(") ");
}
}
//must match index type
sb.Append("+__IndexType:");
sb.Append(type);
var raw = internalSearcher.CreateSearchCriteria().RawQuery(sb.ToString());
var result = internalSearcher
//only return the number of items specified to read up to the amount of records to fill from 0 -> the number of items on the page requested
.Search(raw, Convert.ToInt32(pageSize * (pageIndex + 1)));
totalFound = result.TotalItemCount;
var pagedResult = result.Skip(Convert.ToInt32(pageIndex));
switch (entityType)
{
case UmbracoEntityTypes.Member:
return MemberFromSearchResults(pagedResult.ToArray());
case UmbracoEntityTypes.Media:
return MediaFromSearchResults(pagedResult);
case UmbracoEntityTypes.Document:
return ContentFromSearchResults(umbracoHelper, pagedResult);
default:
throw new NotSupportedException("The " + typeof(UmbracoTreeSearcher) + " currently does not support searching against object type " + entityType);
}
}
/// <summary>
/// Returns a collection of entities for media based on search results
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
private IEnumerable<SearchResultItem> MemberFromSearchResults(SearchResult[] results)
{
var mapped = Mapper.Map<IEnumerable<SearchResultItem>>(results).ToArray();
//add additional data
foreach (var m in mapped)
{
//if no icon could be mapped, it will be set to document, so change it to picture
if (m.Icon == "icon-document")
{
m.Icon = "icon-user";
}
var searchResult = results.First(x => x.Id.ToInvariantString() == m.Id.ToString());
if (searchResult.Fields.ContainsKey("email") && searchResult.Fields["email"] != null)
{
m.AdditionalData["Email"] = results.First(x => x.Id.ToInvariantString() == m.Id.ToString()).Fields["email"];
}
if (searchResult.Fields.ContainsKey("__key") && searchResult.Fields["__key"] != null)
{
Guid key;
if (Guid.TryParse(searchResult.Fields["__key"], out key))
{
m.Key = key;
}
}
}
return mapped;
}
/// <summary>
/// Returns a collection of entities for media based on search results
/// </summary>
/// <param name="results"></param>
/// <returns></returns>
private IEnumerable<SearchResultItem> MediaFromSearchResults(IEnumerable<SearchResult> results)
{
var mapped = Mapper.Map<IEnumerable<SearchResultItem>>(results).ToArray();
//add additional data
foreach (var m in mapped)
{
//if no icon could be mapped, it will be set to document, so change it to picture
if (m.Icon == "icon-document")
{
m.Icon = "icon-picture";
}
}
return mapped;
}
/// <summary>
/// Returns a collection of entities for content based on search results
/// </summary>
/// <param name="umbracoHelper"></param>
/// <param name="results"></param>
/// <returns></returns>
private IEnumerable<SearchResultItem> ContentFromSearchResults(UmbracoHelper umbracoHelper, IEnumerable<SearchResult> results)
{
var mapped = Mapper.Map<IEnumerable<SearchResultItem>>(results).ToArray();
//add additional data
foreach (var m in mapped)
{
var intId = m.Id.TryConvertTo<int>();
if (intId.Success)
{
m.AdditionalData["Url"] = umbracoHelper.NiceUrl(intId.Result);
}
}
return mapped;
}
}
}

View File

@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
@@ -18,6 +19,7 @@ using Umbraco.Web.Mvc;
using Umbraco.Web.WebApi;
using umbraco.BusinessLogic;
using umbraco.cms.presentation.Trees;
using Umbraco.Core.Services;
using ApplicationTree = Umbraco.Core.Models.ApplicationTree;
using IAuthorizationFilter = System.Web.Http.Filters.IAuthorizationFilter;
using UrlHelper = System.Web.Http.Routing.UrlHelper;
@@ -26,6 +28,48 @@ namespace Umbraco.Web.Trees
{
internal static class ApplicationTreeExtensions
{
private static readonly ConcurrentDictionary<Type, TreeAttribute> TreeAttributeCache = new ConcurrentDictionary<Type, TreeAttribute>();
internal static TreeAttribute GetTreeAttribute(this Type treeControllerType)
{
return TreeAttributeCache.GetOrAdd(treeControllerType, type =>
{
//Locate the tree attribute
var treeAttributes = type
.GetCustomAttributes<TreeAttribute>(false)
.ToArray();
if (treeAttributes.Length == 0)
{
throw new InvalidOperationException("The Tree controller is missing the " + typeof(TreeAttribute).FullName + " attribute");
}
//assign the properties of this object to those of the metadata attribute
return treeAttributes[0];
});
}
internal static TreeAttribute GetTreeAttribute(this ApplicationTree tree)
{
return tree.GetRuntimeType().GetTreeAttribute();
}
internal static string GetRootNodeDisplayName(this TreeAttribute attribute, ILocalizedTextService textService)
{
//if title is defined, return that
if (string.IsNullOrEmpty(attribute.Title) == false)
return attribute.Title;
//try to look up a tree header matching the tree alias
var localizedLabel = textService.Localize("treeHeaders/" + attribute.Alias);
if (string.IsNullOrEmpty(localizedLabel) == false)
return localizedLabel;
//is returned to signal that a label was not found
return "[" + attribute.Alias + "]";
}
internal static Attempt<Type> TryGetControllerTree(this ApplicationTree appTree)
{

View File

@@ -229,9 +229,9 @@ namespace Umbraco.Web.Trees
{
var menu = new MenuItemCollection();
menu.Items.Add<ActionNew>(ui.Text("actions", ActionNew.Instance.Alias));
menu.Items.Add<ActionDelete>(ui.Text("actions", ActionDelete.Instance.Alias));
//need to ensure some of these are converted to the legacy system - until we upgrade them all to be angularized.
menu.Items.Add<ActionDelete>(ui.Text("actions", ActionDelete.Instance.Alias));
//need to ensure some of these are converted to the legacy system - until we upgrade them all to be angularized.
menu.Items.Add<ActionMove>(ui.Text("actions", ActionMove.Instance.Alias), true);
menu.Items.Add<ActionCopy>(ui.Text("actions", ActionCopy.Instance.Alias));
menu.Items.Add<ActionChangeDocType>(ui.Text("actions", ActionChangeDocType.Instance.Alias)).ConvertLegacyMenuItem(item, "content", "content");
@@ -244,8 +244,8 @@ namespace Umbraco.Web.Trees
menu.Items.Add<ActionToPublish>(ui.Text("actions", ActionToPublish.Instance.Alias)).ConvertLegacyMenuItem(item, "content", "content");
menu.Items.Add<ActionAssignDomain>(ui.Text("actions", ActionAssignDomain.Instance.Alias)).ConvertLegacyMenuItem(item, "content", "content");
menu.Items.Add<ActionRights>(ui.Text("actions", ActionRights.Instance.Alias)).ConvertLegacyMenuItem(item, "content", "content");
menu.Items.Add<ActionProtect>(ui.Text("actions", ActionProtect.Instance.Alias), true).ConvertLegacyMenuItem(item, "content", "content");
menu.Items.Add<ActionProtect>(ui.Text("actions", ActionProtect.Instance.Alias), true).ConvertLegacyMenuItem(item, "content", "content");
menu.Items.Add<ActionNotify>(ui.Text("actions", ActionNotify.Instance.Alias), true).ConvertLegacyMenuItem(item, "content", "content");
menu.Items.Add<ActionSendToTranslate>(ui.Text("actions", ActionSendToTranslate.Instance.Alias)).ConvertLegacyMenuItem(item, "content", "content");
@@ -254,7 +254,7 @@ namespace Umbraco.Web.Trees
return menu;
}
public IEnumerable<SearchResultItem> Search(string query, int pageSize, int pageIndex, out int totalFound, string searchFrom = null)
public IEnumerable<SearchResultItem> Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null)
{
return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Document, pageSize, pageIndex, out totalFound, searchFrom);
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using AutoMapper;
using umbraco;
using umbraco.BusinessLogic.Actions;
using Umbraco.Core;
@@ -9,6 +11,8 @@ using Umbraco.Core.Models;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.WebApi.Filters;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Search;
namespace Umbraco.Web.Trees
{
@@ -17,7 +21,7 @@ namespace Umbraco.Web.Trees
[Mvc.PluginController("UmbracoTrees")]
[CoreTree]
[LegacyBaseTree(typeof(loadNodeTypes))]
public class ContentTypeTreeController : TreeController
public class ContentTypeTreeController : TreeController, ISearchableTree
{
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
@@ -137,5 +141,11 @@ namespace Umbraco.Web.Trees
return menu;
}
public IEnumerable<SearchResultItem> Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null)
{
var results = Services.EntityService.GetPagedDescendantsFromRoot(UmbracoObjectTypes.DocumentType, pageIndex, pageSize, out totalFound, filter: query);
return Mapper.Map<IEnumerable<SearchResultItem>>(results);
}
}
}

View File

@@ -5,6 +5,7 @@ using System.Linq;
using System.Net;
using System.Net.Http.Formatting;
using System.Web.Http;
using AutoMapper;
using Umbraco.Core;
using Umbraco.Core.Models;
using Umbraco.Web.Models.Trees;
@@ -14,6 +15,8 @@ using umbraco;
using umbraco.BusinessLogic.Actions;
using Umbraco.Core.Models.EntityBase;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Search;
using Constants = Umbraco.Core.Constants;
namespace Umbraco.Web.Trees
@@ -22,7 +25,7 @@ namespace Umbraco.Web.Trees
[Tree(Constants.Applications.Developer, Constants.Trees.DataTypes, null, sortOrder:1)]
[PluginController("UmbracoTrees")]
[CoreTree]
public class DataTypeTreeController : TreeController
public class DataTypeTreeController : TreeController, ISearchableTree
{
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
@@ -123,5 +126,11 @@ namespace Umbraco.Web.Trees
return menu;
}
public IEnumerable<SearchResultItem> Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null)
{
var results = Services.EntityService.GetPagedDescendantsFromRoot(UmbracoObjectTypes.DataType, pageIndex, pageSize, out totalFound, filter: query);
return Mapper.Map<IEnumerable<SearchResultItem>>(results);
}
}
}

View File

@@ -171,7 +171,7 @@ namespace Umbraco.Web.Trees
return Security.CurrentUser.HasPathAccess(media);
}
public IEnumerable<SearchResultItem> Search(string query, int pageSize, int pageIndex, out int totalFound, string searchFrom = null)
public IEnumerable<SearchResultItem> Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null)
{
return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Media, pageSize, pageIndex, out totalFound, searchFrom);
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http.Formatting;
using AutoMapper;
using umbraco;
using umbraco.BusinessLogic.Actions;
using Umbraco.Core;
@@ -9,6 +11,8 @@ using Umbraco.Core.Models;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.WebApi.Filters;
using Umbraco.Core.Services;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Search;
namespace Umbraco.Web.Trees
{
@@ -17,7 +21,7 @@ namespace Umbraco.Web.Trees
[Mvc.PluginController("UmbracoTrees")]
[CoreTree]
[LegacyBaseTree(typeof(loadMediaTypes))]
public class MediaTypeTreeController : TreeController
public class MediaTypeTreeController : TreeController, ISearchableTree
{
protected override TreeNodeCollection GetTreeNodes(string id, FormDataCollection queryStrings)
{
@@ -47,7 +51,7 @@ namespace Umbraco.Web.Trees
.OrderBy(entity => entity.Name)
.Select(dt =>
{
var node = CreateTreeNode(dt, Constants.ObjectTypes.MediaTypeGuid, id, queryStrings, "icon-item-arrangement",
var node = CreateTreeNode(dt, Constants.ObjectTypes.MediaTypeGuid, id, queryStrings, "icon-thumbnails",
//NOTE: Since 7.4+ child type creation is enabled by a config option. It defaults to on, but can be disabled if we decide to.
//We need this check to keep supporting sites where childs have already been created.
dt.HasChildren());
@@ -124,5 +128,11 @@ namespace Umbraco.Web.Trees
return menu;
}
public IEnumerable<SearchResultItem> Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null)
{
var results = Services.EntityService.GetPagedDescendantsFromRoot(UmbracoObjectTypes.MediaType, pageIndex, pageSize, out totalFound, filter: query);
return Mapper.Map<IEnumerable<SearchResultItem>>(results);
}
}
}

View File

@@ -184,7 +184,7 @@ namespace Umbraco.Web.Trees
return menu;
}
public IEnumerable<SearchResultItem> Search(string query, int pageSize, int pageIndex, out int totalFound, string searchFrom = null)
public IEnumerable<SearchResultItem> Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null)
{
return _treeSearcher.ExamineSearch(Umbraco, query, UmbracoEntityTypes.Member, pageSize, pageIndex, out totalFound, searchFrom);
}

View File

@@ -6,14 +6,17 @@ using System.IO;
using System.Linq;
using System.Net.Http.Formatting;
using System.Web.Services.Description;
using AutoMapper;
using umbraco;
using umbraco.BusinessLogic.Actions;
using umbraco.cms.businesslogic.template;
using Umbraco.Core;
using Umbraco.Core.Configuration;
using Umbraco.Core.Models;
using Umbraco.Web.Models.ContentEditing;
using Umbraco.Web.Models.Trees;
using Umbraco.Web.Mvc;
using Umbraco.Web.Search;
using Umbraco.Web.WebApi.Filters;
using Constants = Umbraco.Core.Constants;
@@ -24,7 +27,7 @@ namespace Umbraco.Web.Trees
[Tree(Constants.Applications.Settings, Constants.Trees.Templates, null, sortOrder:1)]
[PluginController("UmbracoTrees")]
[CoreTree]
public class TemplatesTreeController : TreeController
public class TemplatesTreeController : TreeController, ISearchableTree
{
/// <summary>
/// The method called to render the contents of the tree structure
@@ -129,5 +132,11 @@ namespace Umbraco.Web.Trees
Uri.EscapeDataString("settings/editTemplate.aspx?templateID=" + template.Id)
: null;
}
public IEnumerable<SearchResultItem> Search(string query, int pageSize, long pageIndex, out long totalFound, string searchFrom = null)
{
var results = Services.EntityService.GetPagedDescendantsFromRoot(UmbracoObjectTypes.Template, pageIndex, pageSize, out totalFound, filter: query);
return Mapper.Map<IEnumerable<SearchResultItem>>(results);
}
}
}

View File

@@ -40,22 +40,7 @@ namespace Umbraco.Web.Trees
/// </summary>
public override string RootNodeDisplayName
{
get
{
//if title is defined, return that
if(string.IsNullOrEmpty(_attribute.Title) == false)
return _attribute.Title;
//try to look up a tree header matching the tree alias
var localizedLabel = Services.TextService.Localize("treeHeaders/" + _attribute.Alias);
if (string.IsNullOrEmpty(localizedLabel) == false)
return localizedLabel;
//is returned to signal that a label was not found
return "[" + _attribute.Alias + "]";
}
get { return _attribute.GetRootNodeDisplayName(Services.TextService); }
}
/// <summary>
@@ -68,19 +53,7 @@ namespace Umbraco.Web.Trees
private void Initialize()
{
//Locate the tree attribute
var treeAttributes = GetType()
.GetCustomAttributes(typeof(TreeAttribute), false)
.OfType<TreeAttribute>()
.ToArray();
if (treeAttributes.Any() == false)
{
throw new InvalidOperationException("The Tree controller is missing the " + typeof(TreeAttribute).FullName + " attribute");
}
//assign the properties of this object to those of the metadata attribute
_attribute = treeAttributes.First();
_attribute = GetType().GetTreeAttribute();
}
}
}