Merge pull request #2371 from umbraco/temp-U4-10707
fixes: U4-10707 Config to order tour groups
This commit is contained in:
@@ -394,7 +394,7 @@ namespace Umbraco.Core.ObjectResolution
|
||||
/// WARNING! Do not use this unless you know what you are doing, clear all types registered and instances
|
||||
/// created. Typically only used if a resolver is no longer used in an application and memory is to be GC'd
|
||||
/// </summary>
|
||||
internal void ResetCollections()
|
||||
internal virtual void ResetCollections()
|
||||
{
|
||||
using (new WriteLock(_lock))
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
* Registers all tours from the server and returns a promise
|
||||
*/
|
||||
function registerAllTours() {
|
||||
tours = [];
|
||||
return tourResource.getTours().then(function(tourFiles) {
|
||||
angular.forEach(tourFiles, function (tourFile) {
|
||||
angular.forEach(tourFile.tours, function(newTour) {
|
||||
@@ -218,7 +219,38 @@
|
||||
var deferred = $q.defer();
|
||||
var tours = getTours();
|
||||
setTourStatuses(tours).then(function() {
|
||||
var groupedTours = _.groupBy(tours, "group");
|
||||
var groupedTours = [];
|
||||
tours.forEach(function (item) {
|
||||
|
||||
var groupExists = false;
|
||||
var newGroup = {
|
||||
"group": "",
|
||||
"tours": []
|
||||
};
|
||||
|
||||
groupedTours.forEach(function(group){
|
||||
// extend existing group if it is already added
|
||||
if(group.group === item.group) {
|
||||
if(item.groupOrder) {
|
||||
group.groupOrder = item.groupOrder
|
||||
}
|
||||
groupExists = true;
|
||||
group.tours.push(item)
|
||||
}
|
||||
});
|
||||
|
||||
// push new group to array if it doesn't exist
|
||||
if(!groupExists) {
|
||||
newGroup.group = item.group;
|
||||
if(item.groupOrder) {
|
||||
newGroup.groupOrder = item.groupOrder
|
||||
}
|
||||
newGroup.tours.push(item);
|
||||
groupedTours.push(newGroup);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
deferred.resolve(groupedTours);
|
||||
});
|
||||
return deferred.promise;
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
|
||||
function showTourButton(index, tourGroup) {
|
||||
if(index !== 0) {
|
||||
var prevTour = tourGroup[index - 1];
|
||||
var prevTour = tourGroup.tours[index - 1];
|
||||
if(prevTour.completed) {
|
||||
return true;
|
||||
}
|
||||
@@ -147,12 +147,12 @@
|
||||
// Finding out, how many tours are completed for the progress circle
|
||||
angular.forEach(vm.tours, function(group){
|
||||
var completedTours = 0;
|
||||
angular.forEach(group, function(tour){
|
||||
angular.forEach(group.tours, function(tour){
|
||||
if(tour.completed) {
|
||||
completedTours++;
|
||||
}
|
||||
});
|
||||
group.completedPercentage = Math.round((completedTours/group.length)*100);
|
||||
group.completedPercentage = Math.round((completedTours/group.tours.length)*100);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,23 +12,23 @@
|
||||
|
||||
<h5 style="margin-bottom: 10px; margin-top: 0;">Tours</h5>
|
||||
|
||||
<div ng-repeat="(key,value) in vm.tours" style="margin-bottom: 5px;">
|
||||
<div ng-repeat="tourGroup in vm.tours | orderBy:'groupOrder'" style="margin-bottom: 5px;">
|
||||
|
||||
<div class="umb-help-list">
|
||||
|
||||
<a href="" class="umb-help-list-item umb-help-list-item__content flex items-center justify-between" style="text-decoration: none;" ng-click="value.open = !value.open">
|
||||
<h5 class="umb-help-list-item__group-title"><i style="margin-right: 2px;text-decoration: none;" ng-class="{'icon-navigation-right': !value.open, 'icon-navigation-down': value.open}"></i>
|
||||
<span ng-if="key !== 'undefined'">{{key}}</span>
|
||||
<span ng-if="key === 'undefined'">Other</span>
|
||||
<a href="" class="umb-help-list-item umb-help-list-item__content flex items-center justify-between" style="text-decoration: none;" ng-click="tourGroup.open = !tourGroup.open">
|
||||
<h5 class="umb-help-list-item__group-title"><i style="margin-right: 2px;text-decoration: none;" ng-class="{'icon-navigation-right': !tourGroup.open, 'icon-navigation-down': tourGroup.open}"></i>
|
||||
<span ng-if="tourGroup.group !== 'undefined'">{{tourGroup.group}}</span>
|
||||
<span ng-if="tourGroup.group === 'undefined'">Other</span>
|
||||
</h5>
|
||||
<umb-progress-circle
|
||||
percentage="{{value.completedPercentage}}"
|
||||
percentage="{{tourGroup.completedPercentage}}"
|
||||
size="40">
|
||||
</umb-progress-circle>
|
||||
</a>
|
||||
|
||||
<div ng-if="value.open">
|
||||
<div data-element="tour-{{tour.alias}}" class="umb-help-list-item" ng-repeat="tour in value">
|
||||
<div ng-if="tourGroup.open">
|
||||
<div data-element="tour-{{tour.alias}}" class="umb-help-list-item" ng-repeat="tour in tourGroup.tours">
|
||||
<div class="umb-help-list-item__content justify-between">
|
||||
<div class="flex items-center">
|
||||
<div ng-if="!tour.completed" class="umb-number-badge umb-number-badge--xs umb-help-list-item__icon">{{ $index + 1 }}</div>
|
||||
@@ -36,7 +36,7 @@
|
||||
<span ng-class="{'strike': tour.completed}" class="umb-help-list-item__title">{{ tour.name }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<umb-button ng-if="!tour.completed && vm.showTourButton($index, value)" button-style="primary" size="xxs" type="button" label="Start" action="vm.startTour(tour)"></umb-button>
|
||||
<umb-button ng-if="!tour.completed && vm.showTourButton($index, tourGroup)" button-style="primary" size="xxs" type="button" label="Start" action="vm.startTour(tour)"></umb-button>
|
||||
<umb-button ng-if="tour.completed" size="xxs" type="button" label="Rerun" action="vm.startTour(tour)"></umb-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"name": "Introduction",
|
||||
"alias": "umbIntroIntroduction",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"allowDisable": true,
|
||||
"steps": [
|
||||
{
|
||||
@@ -86,113 +87,114 @@
|
||||
"name": "Create document type",
|
||||
"alias": "umbIntroCreateDocType",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"steps": [
|
||||
{
|
||||
title: "Create your first Document Type",
|
||||
content: "<p>Step 1 of any site is to create a <strong>Document Type</strong>.<br> A Document Type is a template for content. For each <em>type</em> of content you want to create you'll create a Document Type. This will define were content based on this Document Type can be created, how many properties it holds and what the input method should be for these properties.</p><p>When you have at least one Document type in place you can start creating content and this content can the be used in a template.</p><p>In this tour you will learn how to set up a basic Document Type with a property to enter a short text.</p>",
|
||||
type: "intro"
|
||||
"title": "Create your first Document Type",
|
||||
"content": "<p>Step 1 of any site is to create a <strong>Document Type</strong>.<br> A Document Type is a template for content. For each <em>type</em> of content you want to create you'll create a Document Type. This will define were content based on this Document Type can be created, how many properties it holds and what the input method should be for these properties.</p><p>When you have at least one Document type in place you can start creating content and this content can the be used in a template.</p><p>In this tour you will learn how to set up a basic Document Type with a property to enter a short text.</p>",
|
||||
"type": "intro"
|
||||
},
|
||||
{
|
||||
element: "#applications [data-element='section-settings']",
|
||||
title: "Navigate to the Settings sections",
|
||||
content: "In the <b>Settings section</b> you can create and manage Document types.",
|
||||
event: "click",
|
||||
backdropOpacity: 0.6
|
||||
"element": "#applications [data-element='section-settings']",
|
||||
"title": "Navigate to the Settings sections",
|
||||
"content": "In the <b>Settings section</b> you can create and manage Document types.",
|
||||
"event": "click",
|
||||
"backdropOpacity": 0.6
|
||||
},
|
||||
{
|
||||
element: "#tree [data-element='tree-item-documentTypes']",
|
||||
title: "Create Document Type",
|
||||
content: "<p>Hover the Document Type tree and click the <b>three small dots</b> to open the <b>context menu</b>.</p>",
|
||||
event: "click",
|
||||
eventElement: "#tree [data-element='tree-item-documentTypes'] [data-element='tree-item-options']"
|
||||
"element": "#tree [data-element='tree-item-documentTypes']",
|
||||
"title": "Create Document Type",
|
||||
"content": "<p>Hover the Document Type tree and click the <b>three small dots</b> to open the <b>context menu</b>.</p>",
|
||||
"event": "click",
|
||||
"eventElement": "#tree [data-element='tree-item-documentTypes'] [data-element='tree-item-options']"
|
||||
},
|
||||
{
|
||||
element: "#dialog [data-element='action-documentType']",
|
||||
title: "Create Document Type",
|
||||
content: "<p>Click <b>Document Type</b> to create a new document type with a template. The template will be automatically created and set as the default template for this Document Type</p><p>You will use the template in a later tour render content.</p>",
|
||||
event: "click"
|
||||
"element": "#dialog [data-element='action-documentType']",
|
||||
"title": "Create Document Type",
|
||||
"content": "<p>Click <b>Document Type</b> to create a new document type with a template. The template will be automatically created and set as the default template for this Document Type</p><p>You will use the template in a later tour render content.</p>",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-name-field']",
|
||||
title: "Enter a name",
|
||||
content: "<p>Your Document Type needs a name. Enter <code>My Home Page</code> in the field and click <b>Next</b>.",
|
||||
view: "doctypename"
|
||||
"element": "[data-element='editor-name-field']",
|
||||
"title": "Enter a name",
|
||||
"content": "<p>Your Document Type needs a name. Enter <code>My Home Page</code> in the field and click <b>Next</b>.",
|
||||
"view": "doctypename"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-description']",
|
||||
title: "Enter a description",
|
||||
content: "<p>A description helps to pick the right document type when creating content.</p><p>Write a description to our Home page. It could be: <br/><pre>The home page of the website</pre></p>"
|
||||
"element": "[data-element='editor-description']",
|
||||
"title": "Enter a description",
|
||||
"content": "<p>A description helps to pick the right document type when creating content.</p><p>Write a description to our Home page. It could be: <br/><pre>The home page of the website</pre></p>"
|
||||
},
|
||||
{
|
||||
element: "[data-element='group-add']",
|
||||
title: "Add tab",
|
||||
content: "Tabs are used to organize properties on content in the Content section. Click <b>Add new tab</b> to add a tab.",
|
||||
event: "click"
|
||||
"element": "[data-element='group-add']",
|
||||
"title": "Add tab",
|
||||
"content": "Tabs are used to organize properties on content in the Content section. Click <b>Add new tab</b> to add a tab.",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element='group-name-field']",
|
||||
title: "Name the tab",
|
||||
content: "<p>Enter <code>Home</code> in the tab name.</p><p><em>You can name a tab anything you want and if you have a lot of properties it can be useful to add multiple tabs.</em></p>",
|
||||
view: "tabName"
|
||||
"element": "[data-element='group-name-field']",
|
||||
"title": "Name the tab",
|
||||
"content": "<p>Enter <code>Home</code> in the tab name.</p><p><em>You can name a tab anything you want and if you have a lot of properties it can be useful to add multiple tabs.</em></p>",
|
||||
"view": "tabName"
|
||||
},
|
||||
{
|
||||
element: "[data-element='property-add']",
|
||||
title: "Add a property",
|
||||
content: "<p>Properties are the different input fields on a content page.</p><p>On our Home Page we wan't to add a welcome text.</p><p>Click <b>Add property</b> to open the property dialog.</p>",
|
||||
event: "click"
|
||||
"element": "[data-element='property-add']",
|
||||
"title": "Add a property",
|
||||
"content": "<p>Properties are the different input fields on a content page.</p><p>On our Home Page we wan't to add a welcome text.</p><p>Click <b>Add property</b> to open the property dialog.</p>",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element~='overlay-property-settings'] [data-element='property-name']",
|
||||
title: "Name the property",
|
||||
content: "Enter <code>Welcome Text</code> as the name for the property.",
|
||||
view: "propertyname"
|
||||
"element": "[data-element~='overlay-property-settings'] [data-element='property-name']",
|
||||
"title": "Name the property",
|
||||
"content": "Enter <code>Welcome Text</code> as the name for the property.",
|
||||
"view": "propertyname"
|
||||
},
|
||||
{
|
||||
element: "[data-element~='overlay-property-settings'] [data-element='property-description']",
|
||||
title: "Enter a description",
|
||||
content: "<p>A description will help to fill in the right content.</p><p>Enter a description for the property editor. It could be:<br/> <pre>Write a nice introduction text so the visitors feel welcome</pre></p>"
|
||||
"element": "[data-element~='overlay-property-settings'] [data-element='property-description']",
|
||||
"title": "Enter a description",
|
||||
"content": "<p>A description will help to fill in the right content.</p><p>Enter a description for the property editor. It could be:<br/> <pre>Write a nice introduction text so the visitors feel welcome</pre></p>"
|
||||
},
|
||||
{
|
||||
element: "[data-element~='overlay-property-settings'] [data-element='editor-add']",
|
||||
title: "Add editor",
|
||||
content: "When you add an editor you choose what the input method for this property will be. Click <b>Add editor</b> to open the editor picker dialog.",
|
||||
event: "click"
|
||||
"element": "[data-element~='overlay-property-settings'] [data-element='editor-add']",
|
||||
"title": "Add editor",
|
||||
"content": "When you add an editor you choose what the input method for this property will be. Click <b>Add editor</b> to open the editor picker dialog.",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element~='overlay-editor-picker']",
|
||||
elementPreventClick: true,
|
||||
title: "Editor picker",
|
||||
content: "<p>In the editor picker dialog we can pick one of the many build in editor.</p><p><em>You can choose from preconfigured data types (Reuse) or create a new configuration (Available editors)</em></p>"
|
||||
"element": "[data-element~='overlay-editor-picker']",
|
||||
"elementPreventClick": true,
|
||||
"title": "Editor picker",
|
||||
"content": "<p>In the editor picker dialog we can pick one of the many build in editor.</p><p><em>You can choose from preconfigured data types (Reuse) or create a new configuration (Available editors)</em></p>"
|
||||
},
|
||||
{
|
||||
element: "[data-element~='overlay-editor-picker'] [data-element='editor-Textarea']",
|
||||
title: "Select editor",
|
||||
content: "Select the <b>Textarea</b> editor. This will add a textarea to the Welcome Text property.",
|
||||
event: "click"
|
||||
"element": "[data-element~='overlay-editor-picker'] [data-element='editor-Textarea']",
|
||||
"title": "Select editor",
|
||||
"content": "Select the <b>Textarea</b> editor. This will add a textarea to the Welcome Text property.",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element~='overlay-editor-settings']",
|
||||
elementPreventClick: true,
|
||||
title: "Editor settings",
|
||||
content: "Each property editor can have individual settings. For the textarea editor you can set a charachter limit but in this case it is not needed"
|
||||
"element": "[data-element~='overlay-editor-settings']",
|
||||
"elementPreventClick": true,
|
||||
"title": "Editor settings",
|
||||
"content": "Each property editor can have individual settings. For the textarea editor you can set a charachter limit but in this case it is not needed"
|
||||
},
|
||||
{
|
||||
element: "[data-element~='overlay-editor-settings'] [data-element='button-overlaySubmit']",
|
||||
title: "Save editor",
|
||||
content: "Click <b>Submit</b> to save the editor.",
|
||||
event: "click"
|
||||
"element": "[data-element~='overlay-editor-settings'] [data-element='button-overlaySubmit']",
|
||||
"title": "Save editor",
|
||||
"content": "Click <b>Submit</b> to save the editor.",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element~='overlay-property-settings'] [data-element='button-overlaySubmit']",
|
||||
title: "Add property to document type",
|
||||
content: "Click <b>Submit</b> to add the property to the document type.",
|
||||
event: "click"
|
||||
"element": "[data-element~='overlay-property-settings'] [data-element='button-overlaySubmit']",
|
||||
"title": "Add property to document type",
|
||||
"content": "Click <b>Submit</b> to add the property to the document type.",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element='button-save']",
|
||||
title: "Save the document type",
|
||||
content: "All we need now is to save the document type. Click <b>Save</b> to create and save your new document type.",
|
||||
event: "click"
|
||||
"element": "[data-element='button-save']",
|
||||
"title": "Save the document type",
|
||||
"content": "All we need now is to save the document type. Click <b>Save</b> to create and save your new document type.",
|
||||
"event": "click"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -200,48 +202,49 @@
|
||||
"name": "Create Content",
|
||||
"alias": "umbIntroCreateContent",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"steps": [
|
||||
{
|
||||
title: "Creating your first content node",
|
||||
content: "<p>In this tour you will learn how to create the home page for your website. It will use the <b>Home Page</b> Document type you created in the previous tour.</p>",
|
||||
type: "intro"
|
||||
"title": "Creating your first content node",
|
||||
"content": "<p>In this tour you will learn how to create the home page for your website. It will use the <b>Home Page</b> Document type you created in the previous tour.</p>",
|
||||
"type": "intro"
|
||||
},
|
||||
{
|
||||
element: "#applications [data-element='section-content']",
|
||||
title: "Navigate to the Content section",
|
||||
content: "<p>In the <b>Content section</b> you can create and manage the content of the website.</p><p>The <b>Content section</b> contains the content of your website. Content is displayed as <b>nodes</b> in the content tree.</p>",
|
||||
event: "click",
|
||||
backdropOpacity: 0.6
|
||||
"element": "#applications [data-element='section-content']",
|
||||
"title": "Navigate to the Content section",
|
||||
"content": "<p>In the <b>Content section</b> you can create and manage the content of the website.</p><p>The <b>Content section</b> contains the content of your website. Content is displayed as <b>nodes</b> in the content tree.</p>",
|
||||
"event": "click",
|
||||
"backdropOpacity": 0.6
|
||||
},
|
||||
{
|
||||
element: "[data-element='tree-root']",
|
||||
title: "Open context menu",
|
||||
content: "<p>Open the context menu by hovering the root of the content section.</p><p>Now click the <b>three small dots</b> to the right.</p>",
|
||||
event: "click",
|
||||
eventElement: "[data-element='tree-root'] [data-element='tree-item-options']"
|
||||
"element": "[data-element='tree-root']",
|
||||
"title": "Open context menu",
|
||||
"content": "<p>Open the context menu by hovering the root of the content section.</p><p>Now click the <b>three small dots</b> to the right.</p>",
|
||||
"event": "click",
|
||||
"eventElement": "[data-element='tree-root'] [data-element='tree-item-options']"
|
||||
},
|
||||
{
|
||||
element: "[data-element='action-create-homePage']",
|
||||
title: "Create Home page",
|
||||
content: "<p>The context menu shows you all the actions that are available on a node</p><p>Click on <b>Home Page</b> to create a new page of type <b>Home Page</b>.</p>",
|
||||
event: "click"
|
||||
"element": "[data-element='action-create-homePage']",
|
||||
"title": "Create Home page",
|
||||
"content": "<p>The context menu shows you all the actions that are available on a node</p><p>Click on <b>Home Page</b> to create a new page of type <b>Home Page</b>.</p>",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-content'] [data-element='editor-name-field']",
|
||||
title: "Give your new page a name",
|
||||
content: "<p>Our new page needs a name. Enter <code>Home</code> in the field and click <b>Next</b>.</p>",
|
||||
view: "nodename"
|
||||
"element": "[data-element='editor-content'] [data-element='editor-name-field']",
|
||||
"title": "Give your new page a name",
|
||||
"content": "<p>Our new page needs a name. Enter <code>Home</code> in the field and click <b>Next</b>.</p>",
|
||||
"view": "nodename"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-content'] [data-element='property-welcomeText']",
|
||||
title: "Add a welcome text",
|
||||
content: "<p>Add content to the <b>Welcome Text</b> field</p><p>If you don't have any ideas here is a start:<br/> <pre>I am learning Umbraco. High Five I Rock #H5IR</pre>.</p>"
|
||||
"element": "[data-element='editor-content'] [data-element='property-welcomeText']",
|
||||
"title": "Add a welcome text",
|
||||
"content": "<p>Add content to the <b>Welcome Text</b> field</p><p>If you don't have any ideas here is a start:<br/> <pre>I am learning Umbraco. High Five I Rock #H5IR</pre>.</p>"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-content'] [data-element='button-saveAndPublish']",
|
||||
title: "Save and Publish",
|
||||
content: "<p>Now click the <b>Save and publish</b> button to save and publish your changes.</p>",
|
||||
event: "click"
|
||||
"element": "[data-element='editor-content'] [data-element='button-saveAndPublish']",
|
||||
"title": "Save and Publish",
|
||||
"content": "<p>Now click the <b>Save and publish</b> button to save and publish your changes.</p>",
|
||||
"event": "click"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -249,44 +252,45 @@
|
||||
"name": "Render in template",
|
||||
"alias": "umbIntroRenderInTemplate",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"steps": [
|
||||
{
|
||||
title: "Render your content in a template",
|
||||
content: "<p>Templating in Umbraco builds on the concept of <b>Razor Views</b> from asp.net MVC. - This tour is a sneak peak on how to write templates in Umbraco.</p><p>In this tour you will learn how to render content from the <b>Home Page</b> document type so you can see the content added to our <b>Home</b> content page.</p>",
|
||||
type: "intro"
|
||||
"title": "Render your content in a template",
|
||||
"content": "<p>Templating in Umbraco builds on the concept of <b>Razor Views</b> from asp.net MVC. - This tour is a sneak peak on how to write templates in Umbraco.</p><p>In this tour you will learn how to render content from the <b>Home Page</b> document type so you can see the content added to our <b>Home</b> content page.</p>",
|
||||
"type": "intro"
|
||||
},
|
||||
{
|
||||
element: "#applications [data-element='section-settings']",
|
||||
title: "Navigate to the Settings section",
|
||||
content: "<p>In the <b>Settings</b> section you will find all the templates</p><p>It is of course also possible to edit all your code files in your favorite code editor.</p>",
|
||||
event: "click",
|
||||
backdropOpacity: 0.6
|
||||
"element": "#applications [data-element='section-settings']",
|
||||
"title": "Navigate to the Settings section",
|
||||
"content": "<p>In the <b>Settings</b> section you will find all the templates</p><p>It is of course also possible to edit all your code files in your favorite code editor.</p>",
|
||||
"event": "click",
|
||||
"backdropOpacity": 0.6
|
||||
},
|
||||
{
|
||||
element: "#tree [data-element='tree-item-templates']",
|
||||
title: "Expand the Templates node",
|
||||
content: "<p>To see all our templates click the <b>small triangle</b> to the left of the templates node.</p>",
|
||||
event: "click",
|
||||
eventElement: "#tree [data-element='tree-item-templates'] [data-element='tree-item-expand']",
|
||||
view: "templatetree"
|
||||
"element": "#tree [data-element='tree-item-templates']",
|
||||
"title": "Expand the Templates node",
|
||||
"content": "<p>To see all our templates click the <b>small triangle</b> to the left of the templates node.</p>",
|
||||
"event": "click",
|
||||
"eventElement": "#tree [data-element='tree-item-templates'] [data-element='tree-item-expand']",
|
||||
"view": "templatetree"
|
||||
},
|
||||
{
|
||||
element: "#tree [data-element='tree-item-templates'] [data-element='tree-item-Home Page']",
|
||||
title: "Open Home template",
|
||||
content: "<p>Click the <b>Home Page</b> template to open and edit it.</p>",
|
||||
eventElement: "#tree [data-element='tree-item-templates'] [data-element='tree-item-Home Page'] a.umb-tree-item__label",
|
||||
event: "click"
|
||||
"element": "#tree [data-element='tree-item-templates'] [data-element='tree-item-Home Page']",
|
||||
"title": "Open Home template",
|
||||
"content": "<p>Click the <b>Home Page</b> template to open and edit it.</p>",
|
||||
"eventElement": "#tree [data-element='tree-item-templates'] [data-element='tree-item-Home Page'] a.umb-tree-item__label",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-templates'] [data-element='code-editor']",
|
||||
title: "Edit template",
|
||||
content: '<p>The template can be edited here or in your favorite code editor.</p><p>To render the field from the document type add the following to the template:<br/> <pre><h1>@Model.Content.Name</h1><br><p>@Model.Content.WelcomeText</p></pre></p>'
|
||||
"element": "[data-element='editor-templates'] [data-element='code-editor']",
|
||||
"title": "Edit template",
|
||||
"content": "<p>The template can be edited here or in your favorite code editor.</p><p>To render the field from the document type add the following to the template:<br/> <pre><h1>@Model.Content.Name</h1><br><p>@Model.Content.WelcomeText</p></pre></p>"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-templates'] [data-element='button-save']",
|
||||
title: "Save the template",
|
||||
content: "Click the <b>Save button</b> and your template will be saved.",
|
||||
event: "click"
|
||||
"element": "[data-element='editor-templates'] [data-element='button-save']",
|
||||
"title": "Save the template",
|
||||
"content": "Click the <b>Save button</b> and your template will be saved.",
|
||||
"event": "click"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -294,38 +298,39 @@
|
||||
"name": "View Home page",
|
||||
"alias": "umbIntroViewHomePage",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"steps": [
|
||||
{
|
||||
title: "View your Umbraco site",
|
||||
content: "<p>Our three main components to a page is done: <b>Document type, Template, and Content</b> - it is now time to see the result.</p><p>In this tour you will learn how to see your published website.</p>",
|
||||
type: "intro"
|
||||
"title": "View your Umbraco site",
|
||||
"content": "<p>Our three main components to a page is done: <b>Document type, Template, and Content</b> - it is now time to see the result.</p><p>In this tour you will learn how to see your published website.</p>",
|
||||
"type": "intro"
|
||||
},
|
||||
{
|
||||
element: "#applications [data-element='section-content']",
|
||||
title: "Navigate to the content sections",
|
||||
content: "In the <b>Content section</b> you will find the content of our website.",
|
||||
event: "click",
|
||||
backdropOpacity: 0.6
|
||||
"element": "#applications [data-element='section-content']",
|
||||
"title": "Navigate to the content sections",
|
||||
"content": "In the <b>Content section</b> you will find the content of our website.",
|
||||
"event": "click",
|
||||
"backdropOpacity": 0.6
|
||||
},
|
||||
{
|
||||
element: "#tree [data-element='tree-item-Home']",
|
||||
title: "Open the Home page",
|
||||
content: "<p>Click the <b>Home</b> page to open it</p>",
|
||||
event: "click",
|
||||
eventElement: "#tree [data-element='tree-item-Home'] a.umb-tree-item__label"
|
||||
"element": "#tree [data-element='tree-item-Home']",
|
||||
"title": "Open the Home page",
|
||||
"content": "<p>Click the <b>Home</b> page to open it</p>",
|
||||
"event": "click",
|
||||
"eventElement": "#tree [data-element='tree-item-Home'] a.umb-tree-item__label"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-content'] [data-element='tab-_umb_infoTab']",
|
||||
title: "Info",
|
||||
content: "<p>Under the info tab you will find the default information about a content item.</p>",
|
||||
event: "click"
|
||||
"element": "[data-element='editor-content'] [data-element='tab-_umb_infoTab']",
|
||||
"title": "Info",
|
||||
"content": "<p>Under the info tab you will find the default information about a content item.</p>",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-content'] [data-element='node-info-urls']",
|
||||
title: "Open page",
|
||||
content: "<p>Click the <b>Link to document</b> <i class='icon-out'></i> to view your page.</p><p>Tip: Click the preview button in the bottom right corner to preview changes without publishing them.</p>",
|
||||
event: "click",
|
||||
eventElement: "[data-element='editor-content'] [data-element='node-info-urls'] a[target='_blank']"
|
||||
"element": "[data-element='editor-content'] [data-element='node-info-urls']",
|
||||
"title": "Open page",
|
||||
"content": "<p>Click the <b>Link to document</b> <i class='icon-out'></i> to view your page.</p><p>Tip: Click the preview button in the bottom right corner to preview changes without publishing them.</p>",
|
||||
"event": "click",
|
||||
"eventElement": "[data-element='editor-content'] [data-element='node-info-urls'] a[target='_blank']"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -333,86 +338,87 @@
|
||||
"name": "The Media library",
|
||||
"alias": "umbIntroMediaSection",
|
||||
"group": "Getting Started",
|
||||
"groupOrder": 100,
|
||||
"steps": [
|
||||
{
|
||||
title: "How to use the media library",
|
||||
content: "<p>A website would be boring without media content. In Umbraco you can manage all your images, documents, videos etc. in the <b>Media section</b>. Here you can upload and organise your media items and see details about each item.</p><p>In this tour you will learn how to upload and organise your Media library in Umbraco. It will also show you how to view details about a specific media item.</p>",
|
||||
type: "intro"
|
||||
"title": "How to use the media library",
|
||||
"content": "<p>A website would be boring without media content. In Umbraco you can manage all your images, documents, videos etc. in the <b>Media section</b>. Here you can upload and organise your media items and see details about each item.</p><p>In this tour you will learn how to upload and organise your Media library in Umbraco. It will also show you how to view details about a specific media item.</p>",
|
||||
"type": "intro"
|
||||
},
|
||||
{
|
||||
element: "#applications [data-element='section-media']",
|
||||
title: "Navigate to the Media section",
|
||||
content: "The <b>media</b> section is where you manage all your media items.",
|
||||
event: "click",
|
||||
backdropOpacity: 0.6
|
||||
"element": "#applications [data-element='section-media']",
|
||||
"title": "Navigate to the Media section",
|
||||
"content": "The <b>media</b> section is where you manage all your media items.",
|
||||
"event": "click",
|
||||
"backdropOpacity": 0.6
|
||||
},
|
||||
{
|
||||
element: "#tree [data-element='tree-root']",
|
||||
title: "Create a new folder",
|
||||
content: "<p>First create a folder for your images. Hover the media root node and click the <b>three small dots</b> on the right side of the item.</p>",
|
||||
event: "click",
|
||||
eventElement: "#tree [data-element='tree-root'] [data-element='tree-item-options']"
|
||||
"element": "#tree [data-element='tree-root']",
|
||||
"title": "Create a new folder",
|
||||
"content": "<p>First create a folder for your images. Hover the media root node and click the <b>three small dots</b> on the right side of the item.</p>",
|
||||
"event": "click",
|
||||
"eventElement": "#tree [data-element='tree-root'] [data-element='tree-item-options']"
|
||||
},
|
||||
{
|
||||
element: "#dialog [data-element='action-Folder']",
|
||||
title: "Create a new folder",
|
||||
content: "<p>Select the <b>Folder</b> option to select the type folder.</p>",
|
||||
event: "click"
|
||||
"element": "#dialog [data-element='action-Folder']",
|
||||
"title": "Create a new folder",
|
||||
"content": "<p>Select the <b>Folder</b> option to select the type folder.</p>",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-media'] [data-element='editor-name-field']",
|
||||
title: "Enter a name",
|
||||
content: "<p>Enter <code>My Images</code> in the field.</p>"
|
||||
"element": "[data-element='editor-media'] [data-element='editor-name-field']",
|
||||
"title": "Enter a name",
|
||||
"content": "<p>Enter <code>My Images</code> in the field.</p>"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-media'] [data-element='button-save']",
|
||||
title: "Save the folder",
|
||||
content: "<p>Click the <b>Save</b> button to create the new folder</p>",
|
||||
event: "click"
|
||||
"element": "[data-element='editor-media'] [data-element='button-save']",
|
||||
"title": "Save the folder",
|
||||
"content": "<p>Click the <b>Save</b> button to create the new folder</p>",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-media'] [data-element='dropzone']",
|
||||
title: "Upload images",
|
||||
content: "<p>In the upload area you can upload your media items.</p><p>Click the <b>Click here to choose files</b>-button and select a couple of images on your computer and upload them.</p>",
|
||||
view: "uploadimages"
|
||||
"element": "[data-element='editor-media'] [data-element='dropzone']",
|
||||
"title": "Upload images",
|
||||
"content": "<p>In the upload area you can upload your media items.</p><p>Click the <b>Click here to choose files</b>-button and select a couple of images on your computer and upload them.</p>",
|
||||
"view": "uploadimages"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-media'] [data-element='media-grid-item-0']",
|
||||
title: "View media item details",
|
||||
content: "Hover the media item and <b>Click the purple bar</b> to view details about the media item",
|
||||
event: "click",
|
||||
eventElement: "[data-element='editor-media'] [data-element='media-grid-item-0'] [data-element='media-grid-item-edit']"
|
||||
"element": "[data-element='editor-media'] [data-element='media-grid-item-0']",
|
||||
"title": "View media item details",
|
||||
"content": "Hover the media item and <b>Click the purple bar</b> to view details about the media item",
|
||||
"event": "click",
|
||||
"eventElement": "[data-element='editor-media'] [data-element='media-grid-item-0'] [data-element='media-grid-item-edit']"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-media'] [data-element='property-umbracoFile']",
|
||||
title: "The uploaded image",
|
||||
content: "<p>Here you can see the image you have uploaded.</p>"
|
||||
"element": "[data-element='editor-media'] [data-element='property-umbracoFile']",
|
||||
"title": "The uploaded image",
|
||||
"content": "<p>Here you can see the image you have uploaded.</p>"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-media'] [data-element='property-umbracoBytes']",
|
||||
title: "Image size",
|
||||
content: "<p>You will also find other details about the image, like the size.</p><p>Media items work in much the same way as content. So you can add extra properties to an image by creating or editing the <b>Media types</b> in the Settings section.</p>"
|
||||
"element": "[data-element='editor-media'] [data-element='property-umbracoBytes']",
|
||||
"title": "Image size",
|
||||
"content": "<p>You will also find other details about the image, like the size.</p><p>Media items work in much the same way as content. So you can add extra properties to an image by creating or editing the <b>Media types</b> in the Settings section.</p>"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-media'] [data-element='tab-_umb_infoTab']",
|
||||
title: "Info",
|
||||
content: "Like the content section you can also find default information about the media item. You will find these under the info tab.",
|
||||
event: "click"
|
||||
"element": "[data-element='editor-media'] [data-element='tab-_umb_infoTab']",
|
||||
"title": "Info",
|
||||
"content": "Like the content section you can also find default information about the media item. You will find these under the info tab.",
|
||||
"event": "click"
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-media'] [data-element='node-info-urls']",
|
||||
title: "Link to media",
|
||||
content: "The path to the media item..."
|
||||
"element": "[data-element='editor-media'] [data-element='node-info-urls']",
|
||||
"title": "Link to media",
|
||||
"content": "The path to the media item..."
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-media'] [data-element='node-info-update-date']",
|
||||
title: "Last edited",
|
||||
content: "...and information about when the media item has been created and edited."
|
||||
"element": "[data-element='editor-media'] [data-element='node-info-update-date']",
|
||||
"title": "Last edited",
|
||||
"content": "...and information about when the media item has been created and edited."
|
||||
},
|
||||
{
|
||||
element: "[data-element='editor-container']",
|
||||
title: "Using media items",
|
||||
content: "You can reference a media item directly in a template by using the path or try adding a Media Picker to a document type property so you can select media items from the content section."
|
||||
"element": "[data-element='editor-container']",
|
||||
"title": "Using media items",
|
||||
"content": "You can reference a media item directly in a template by using the path or try adding a Media Picker to a document type property so you can select media items from the content section."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2,9 +2,7 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Umbraco.Core.Configuration;
|
||||
using Umbraco.Core.IO;
|
||||
using Umbraco.Web.Models;
|
||||
@@ -25,24 +23,36 @@ namespace Umbraco.Web.Editors
|
||||
if (UmbracoConfig.For.UmbracoSettings().BackOffice.Tours.EnableTours == false)
|
||||
return result;
|
||||
|
||||
var coreTourFiles = Directory.GetFiles(
|
||||
Path.Combine(IOHelper.MapPath(SystemDirectories.Config), "BackOfficeTours"), "*.json");
|
||||
var filters = TourFilterResolver.Current.Filters.ToList();
|
||||
|
||||
//get all filters that will be applied to all tour aliases
|
||||
var aliasOnlyFilters = filters.Where(x => x.PluginName == null && x.TourFileName == null).ToList();
|
||||
|
||||
foreach (var tourFile in coreTourFiles)
|
||||
//don't pass in any filters for core tours that have a plugin name assigned
|
||||
var nonPluginFilters = filters.Where(x => x.PluginName == null).ToList();
|
||||
|
||||
//add core tour files
|
||||
var coreToursPath = Path.Combine(IOHelper.MapPath(SystemDirectories.Config), "BackOfficeTours");
|
||||
if (Directory.Exists(coreToursPath))
|
||||
{
|
||||
var contents = File.ReadAllText(tourFile);
|
||||
|
||||
result.Add(new BackOfficeTourFile
|
||||
foreach (var tourFile in Directory.EnumerateFiles(coreToursPath, "*.json"))
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(tourFile),
|
||||
Tours = JsonConvert.DeserializeObject<BackOfficeTour[]>(contents)
|
||||
});
|
||||
TryParseTourFile(tourFile, result, nonPluginFilters, aliasOnlyFilters);
|
||||
}
|
||||
}
|
||||
|
||||
//collect all tour files in packges
|
||||
//collect all tour files in packages
|
||||
foreach (var plugin in Directory.EnumerateDirectories(IOHelper.MapPath(SystemDirectories.AppPlugins)))
|
||||
{
|
||||
var pluginName = Path.GetFileName(plugin.TrimEnd('\\'));
|
||||
var pluginFilters = filters.Where(x => x.PluginName != null && x.PluginName.IsMatch(pluginName)).ToList();
|
||||
|
||||
//If there is any filter applied to match the plugin only (no file or tour alias) then ignore the plugin entirely
|
||||
var isPluginFiltered = pluginFilters.Any(x => x.TourFileName == null && x.TourAlias == null);
|
||||
if (isPluginFiltered) continue;
|
||||
|
||||
//combine matched package filters with filters not specific to a package
|
||||
var combinedFilters = nonPluginFilters.Concat(pluginFilters).ToList();
|
||||
|
||||
foreach (var backofficeDir in Directory.EnumerateDirectories(plugin, "backoffice"))
|
||||
{
|
||||
@@ -50,13 +60,7 @@ namespace Umbraco.Web.Editors
|
||||
{
|
||||
foreach (var tourFile in Directory.EnumerateFiles(tourDir, "*.json"))
|
||||
{
|
||||
var contents = File.ReadAllText(tourFile);
|
||||
result.Add(new BackOfficeTourFile
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(tourFile),
|
||||
PluginName = pluginName,
|
||||
Tours = JsonConvert.DeserializeObject<BackOfficeTour[]>(contents)
|
||||
});
|
||||
TryParseTourFile(tourFile, result, combinedFilters, aliasOnlyFilters, pluginName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,5 +68,54 @@ namespace Umbraco.Web.Editors
|
||||
|
||||
return result.OrderBy(x => x.FileName, StringComparer.InvariantCultureIgnoreCase);
|
||||
}
|
||||
|
||||
private void TryParseTourFile(string tourFile,
|
||||
ICollection<BackOfficeTourFile> result,
|
||||
List<BackOfficeTourFilter> filters,
|
||||
List<BackOfficeTourFilter> aliasOnlyFilters,
|
||||
string pluginName = null)
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension(tourFile);
|
||||
if (fileName == null) return;
|
||||
|
||||
//get the filters specific to this file
|
||||
var fileFilters = filters.Where(x => x.TourFileName != null && x.TourFileName.IsMatch(fileName)).ToList();
|
||||
|
||||
//If there is any filter applied to match the file only (no tour alias) then ignore the file entirely
|
||||
var isFileFiltered = fileFilters.Any(x => x.TourAlias == null);
|
||||
if (isFileFiltered) return;
|
||||
|
||||
//now combine all aliases to filter below
|
||||
var aliasFilters = aliasOnlyFilters.Concat(filters.Where(x => x.TourAlias != null))
|
||||
.Select(x => x.TourAlias)
|
||||
.ToList();
|
||||
|
||||
try
|
||||
{
|
||||
var contents = File.ReadAllText(tourFile);
|
||||
var tours = JsonConvert.DeserializeObject<BackOfficeTour[]>(contents);
|
||||
|
||||
var tour = new BackOfficeTourFile
|
||||
{
|
||||
FileName = Path.GetFileNameWithoutExtension(tourFile),
|
||||
PluginName = pluginName,
|
||||
Tours = tours
|
||||
.Where(x => aliasFilters.Count == 0 || aliasFilters.All(filter => filter.IsMatch(x.Alias)) == false)
|
||||
.ToArray()
|
||||
};
|
||||
|
||||
//don't add if all of the tours are filtered
|
||||
if (tour.Tours.Any())
|
||||
result.Add(tour);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new IOException("Error while trying to read file: " + tourFile, e);
|
||||
}
|
||||
catch (JsonReaderException e)
|
||||
{
|
||||
throw new JsonReaderException("Error while trying to parse content as tour data: " + tourFile, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,10 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A model representing a tour.
|
||||
/// </summary>
|
||||
[DataContract(Name = "tour", Namespace = "")]
|
||||
public class BackOfficeTour
|
||||
{
|
||||
@@ -15,6 +14,8 @@ namespace Umbraco.Web.Models
|
||||
public string Alias { get; set; }
|
||||
[DataMember(Name = "group")]
|
||||
public string Group { get; set; }
|
||||
[DataMember(Name = "groupOrder")]
|
||||
public int GroupOrder { get; set; }
|
||||
[DataMember(Name = "allowDisable")]
|
||||
public bool AllowDisable { get; set; }
|
||||
[DataMember(Name = "steps")]
|
||||
|
||||
@@ -3,6 +3,9 @@ using System.Runtime.Serialization;
|
||||
|
||||
namespace Umbraco.Web.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A model representing the file used to load a tour.
|
||||
/// </summary>
|
||||
[DataContract(Name = "tourFile", Namespace = "")]
|
||||
public class BackOfficeTourFile
|
||||
{
|
||||
|
||||
61
src/Umbraco.Web/Models/BackOfficeTourFilter.cs
Normal file
61
src/Umbraco.Web/Models/BackOfficeTourFilter.cs
Normal file
@@ -0,0 +1,61 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Umbraco.Web.Models
|
||||
{
|
||||
public class BackOfficeTourFilter
|
||||
{
|
||||
public Regex PluginName { get; private set; }
|
||||
public Regex TourFileName { get; private set; }
|
||||
public Regex TourAlias { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter to filter out a whole plugin's tours
|
||||
/// </summary>
|
||||
/// <param name="pluginName"></param>
|
||||
/// <returns></returns>
|
||||
public static BackOfficeTourFilter FilterPlugin(Regex pluginName)
|
||||
{
|
||||
return new BackOfficeTourFilter(pluginName, null, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter to filter out a whole tour file
|
||||
/// </summary>
|
||||
/// <param name="tourFileName"></param>
|
||||
/// <returns></returns>
|
||||
public static BackOfficeTourFilter FilterFile(Regex tourFileName)
|
||||
{
|
||||
return new BackOfficeTourFilter(null, tourFileName, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a filter to filter out a tour alias, this will filter out the same alias found in all files
|
||||
/// </summary>
|
||||
/// <param name="tourAlias"></param>
|
||||
/// <returns></returns>
|
||||
public static BackOfficeTourFilter FilterAlias(Regex tourAlias)
|
||||
{
|
||||
return new BackOfficeTourFilter(null, null, tourAlias);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor to create a tour filter
|
||||
/// </summary>
|
||||
/// <param name="pluginName">Value to filter out tours by a plugin, can be null</param>
|
||||
/// <param name="tourFileName">Value to filter out a tour file, can be null</param>
|
||||
/// <param name="tourAlias">Value to filter out a tour alias, can be null</param>
|
||||
/// <remarks>
|
||||
/// Depending on what is null will depend on how the filter is applied.
|
||||
/// If pluginName is not NULL and it's matched then we check if tourFileName is not NULL and it's matched then we check tour alias is not NULL and then match it,
|
||||
/// if any steps is NULL then the filters upstream are applied.
|
||||
/// Example, pluginName = "hello", tourFileName="stuff", tourAlias=NULL = we will filter out the tour file "stuff" from the plugin "hello" but not from other plugins if the same file name exists.
|
||||
/// Example, tourAlias="test.*" = we will filter out all tour aliases that start with the word "test" regardless of the plugin or file name
|
||||
/// </remarks>
|
||||
public BackOfficeTourFilter(Regex pluginName, Regex tourFileName, Regex tourAlias)
|
||||
{
|
||||
PluginName = pluginName;
|
||||
TourFileName = tourFileName;
|
||||
TourAlias = tourAlias;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace Umbraco.Web.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// A model representing a step in a tour.
|
||||
/// </summary>
|
||||
[DataContract(Name = "step", Namespace = "")]
|
||||
public class BackOfficeTourStep
|
||||
{
|
||||
|
||||
79
src/Umbraco.Web/TourFilterResolver.cs
Normal file
79
src/Umbraco.Web/TourFilterResolver.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Umbraco.Core.Logging;
|
||||
using Umbraco.Core.ObjectResolution;
|
||||
using Umbraco.Web.Models;
|
||||
|
||||
namespace Umbraco.Web
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows for adding filters for tours during startup
|
||||
/// </summary>
|
||||
public class TourFilterResolver : ManyObjectsResolverBase<TourFilterResolver, BackOfficeTourFilter>
|
||||
{
|
||||
public TourFilterResolver(IServiceProvider serviceProvider, ILogger logger) : base(serviceProvider, logger)
|
||||
{
|
||||
}
|
||||
|
||||
private readonly HashSet<BackOfficeTourFilter> _instances = new HashSet<BackOfficeTourFilter>();
|
||||
|
||||
public IEnumerable<BackOfficeTourFilter> Filters
|
||||
{
|
||||
get { return Values; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a filter instance
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
public void AddFilter(BackOfficeTourFilter filter)
|
||||
{
|
||||
using (Resolution.Configuration)
|
||||
_instances.Add(filter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a filter instance
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
public void RemoveFilter(BackOfficeTourFilter filter)
|
||||
{
|
||||
using (Resolution.Configuration)
|
||||
_instances.Remove(filter);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a filter instance based on callback
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
public void RemoveFilterWhere(Func<BackOfficeTourFilter, bool> filter)
|
||||
{
|
||||
using (Resolution.Configuration)
|
||||
_instances.RemoveWhere(new Predicate<BackOfficeTourFilter>(filter));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <summary>
|
||||
/// Overridden to return the combined created instances based on the resolved Types and the Concrete values added with AddFilter
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected override IEnumerable<BackOfficeTourFilter> CreateInstances()
|
||||
{
|
||||
var createdInstances = base.CreateInstances();
|
||||
return createdInstances.Concat(_instances);
|
||||
}
|
||||
|
||||
public override void Clear()
|
||||
{
|
||||
base.Clear();
|
||||
_instances.Clear();
|
||||
}
|
||||
|
||||
internal override void ResetCollections()
|
||||
{
|
||||
base.ResetCollections();
|
||||
_instances.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -330,6 +330,8 @@
|
||||
<Compile Include="Editors\ParameterSwapControllerActionSelector.cs" />
|
||||
<Compile Include="Editors\CodeFileController.cs" />
|
||||
<Compile Include="Editors\TourController.cs" />
|
||||
<Compile Include="Models\BackOfficeTourFilter.cs" />
|
||||
<Compile Include="TourFilterResolver.cs" />
|
||||
<Compile Include="Editors\UserEditorAuthorizationHelper.cs" />
|
||||
<Compile Include="Editors\UserGroupAuthorizationAttribute.cs" />
|
||||
<Compile Include="Editors\UserGroupEditorAuthorizationHelper.cs" />
|
||||
|
||||
@@ -356,6 +356,8 @@ namespace Umbraco.Web
|
||||
|
||||
FeaturesResolver.Current = new FeaturesResolver(new UmbracoFeatures());
|
||||
|
||||
TourFilterResolver.Current = new TourFilterResolver(ServiceProvider, LoggerResolver.Current.Logger);
|
||||
|
||||
SearchableTreeResolver.Current = new SearchableTreeResolver(ServiceProvider, LoggerResolver.Current.Logger, ApplicationContext.Services.ApplicationTreeService, () => PluginManager.ResolveSearchableTrees());
|
||||
|
||||
XsltExtensionsResolver.Current = new XsltExtensionsResolver(ServiceProvider, LoggerResolver.Current.Logger, () => PluginManager.ResolveXsltExtensions());
|
||||
|
||||
Reference in New Issue
Block a user