diff --git a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js index f160a6d343..51c28be0ea 100644 --- a/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js +++ b/src/Umbraco.Web.UI.Client/src/common/services/tour.service.js @@ -1,430 +1,122 @@ +/** + @ngdoc service + * @name umbraco.services.tourService + * + * @description + * Added in Umbraco 7.8. Application-wide service for handling tours. + */ (function () { 'use strict'; function tourService(eventsService, currentUserResource, $q) { + var tours = []; var currentTour = null; + /** + * @ngdoc method + * @name umbraco.services.tourService#registerTour + * @methodOf umbraco.services.tourService + * + * @description + * Registers a tour in the service + * @param {Object} tour The tour you want to register in the service + * @param {String} tour.name The tour name + * @param {String} tour.alias The tour alias + * @param {Array} tour.steps Array of tour steps + * @param {String} tour.step.title Step title + * @param {DomElement} tour.step.content Step content (pass in any HTML markup) + * @param {DomElement} tour.step.element Highlight a DOM-element + * @param {Boolean} tour.step.elementPreventClick Adds invisible layer on top of highligted element to prevent all clicks and interaction with it + * @param {Number} tour.step.backdropOpacity Sets the backdrop opacity (default 0.4) + */ + function registerTour(newTour) { + validateTour(newTour); + validateTourRegistration(newTour); + tours.push(newTour); + eventsService.emit("appState.tour.updatedTours", tours); + } + + /** + * @ngdoc method + * @name umbraco.services.tourService#registerTours + * @methodOf umbraco.services.tourService + * + * @description + * Registers an array of tours in the service + * @param {Array} tours The tours to register in the service + */ + function registerTours(newTours) { + angular.forEach(newTours, function(newTour){ + validateTour(newTour); + validateTourRegistration(newTour); + tours.push(newTour); + }); + eventsService.emit("appState.tour.updatedTours", tours); + } + + /** + * @ngdoc method + * @name umbraco.services.tourService#unregisterTour + * @methodOf umbraco.services.tourService + * + * @description + * Unregisters a tour in the service + * @param {String} tourAlias The tour alias of the tour you want to unregister + */ + function unregisterTour(tourAlias) { + tours = tours.filter(function( obj ) { + return obj.alias !== tourAlias; + }); + eventsService.emit("appState.tour.updatedTours", tours); + } + + /** + * @ngdoc method + * @name umbraco.services.tourService#unregisterTourGroup + * @methodOf umbraco.services.tourService + * + * @description + * Unregisters a tour in the service + * @param {String} tourGroupName The name of the tour group you want to unregister + */ + function unregisterTourGroup(tourGroup) { + tours = tours.filter(function( obj ) { + return obj.group !== tourGroup; + }); + eventsService.emit("appState.tour.updatedTours", tours); + } + /** * Method to return all of the tours as a new instance */ function getTours() { - var tours = [ - { - "name": "Introduction", - "alias": "umbIntroIntroduction", - "group": "Getting Started", - "allowDisable": true, - "steps": [ - { - title: "Welcome to Umbraco - The Friendly CMS", - content: "
Thank you for choosing Umbraco - we think this could be the beginning of something beautiful. While it may feel overwhelming at first, we've done a lot to make the learning curve as smooth and fast as possible.
In this quick tour we will introduce you to the main areas of Umbraco and show you how to best get started.
", - type: "intro" - }, - { - element: "#applications", - elementPreventClick: true, - title: "Sections", - content: "These are the Sections and allows you to navigate the different areas of Umbraco.", - backdropOpacity: 0.6 - }, - - { - element: "#tree", - elementPreventClick: true, - title: "The Tree", - content: "This is the Tree and will contain all the content of your website." - }, - { - element: "[data-element='editor-content']", - elementPreventClick: true, - title: "Dashboards", - content: "A dashboard is the main view you are presented with when entering a section within the backoffice, and can be used to show valuable information to the users of the system." - }, - { - element: "[data-element='global-search-field']", - title: "Search", - content: "The search allows you to quickly find content across sections within Umbraco." - }, - { - element: "#applications [data-element='section-user']", - title: "User profile", - content: "Click on the user photo to open the user profile dialog.", - event: "click", - backdropOpacity: 0.6 - }, - { - element: "[data-element~='overlay-user']", - elementPreventClick: true, - title: "User profile", - content: "This is where you can see details about your user, change your password and log out of Umbraco.
In the User section you will be able to do more advaned user management.
" - }, - { - element: "[data-element~='overlay-user'] [data-element='button-overlayClose']", - title: "User profile", - content: "Let's close the user profile again", - event: "click" - }, - { - element: "#applications [data-element='section-help']", - title: "Help", - content: "If you ever find yourself in trouble click here to open the help drawer.", - event: "click", - backdropOpacity: 0.6 - }, - { - element: "[data-element='drawer']", - elementPreventClick: true, - title: "Help", - content: "In the help drawer you will find articles and videos related to the section you are using.
This is also where you will find the next tour on how to get started with Umbraco.
", - backdropOpacity: 0.6 - }, - { - element: "[data-element='drawer'] [data-element='help-tours']", - title: "Tours", - content: "To continue your journey on getting started with Umbraco, you can find more tours right here." - } - ] - }, - { - "name": "Create document type", - "alias": "umbIntroCreateDocType", - "group": "Getting Started", - "steps": [ - { - title: "Create your first Document Type", - content: "Step 1 of any site is to create a Document Type. A Document Type is a data container where you can add data fields. The editor can then input data and Umbraco can use it to output it in the relevant parts of a template.
In this tour you will learn how to set up a basic Document Type with a data field to enter a short text.
", - type: "intro" - }, - { - element: "#applications [data-element='section-settings']", - title: "Navigate to the settings sections", - content: "In the Settings section we will find the document types.", - event: "click", - backdropOpacity: 0.6 - }, - { - element: "#tree [data-element='tree-item-documentTypes']", - title: "Create document type", - content: "Hover the document types tree and click the three small dots to open the context menu.
", - 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: "Click Document Type to create a new document type with a template.
We will use the template in a later tour when we need to render our content.
", - event: "click" - }, - { - element: "[data-element='editor-name-field']", - title: "Enter a name", - content: "Our document type needs a name. Enter Home in the field and click Next.",
- view: "doctypename"
- },
- {
- element: "[data-element='editor-description']",
- title: "Enter a description",
- content: "
A description helps to pick the right document type when creating content.
Write a description to our Home page. It could be:
The home to our website" - }, - { - element: "[data-element='group-add']", - title: "Add tab", - content: "Tabs help us organize the content on a content page. Click Add new tab to add a tab.", - event: "click" - }, - { - element: "[data-element='group-name-field']", - title: "Enter a name", - content: "Enter
Content in the tab name.",
- view: "tabName"
- },
- {
- element: "[data-element='property-add']",
- title: "Add a property",
- content: "Properties are the different types of data on our content page.
On our Home page we wan't to add a welcome text.
Click Add property to open the property dialog.
", - event: "click" - }, - { - element: "[data-element~='overlay-property-settings'] [data-element='property-name']", - title: "Enter a name", - content: "EnterWelcome Text as name for the property.",
- view: "propertyname"
- },
- {
- element: "[data-element~='overlay-property-settings'] [data-element='property-description']",
- title: "Enter a description",
- content: "A description will help to fill in the right content.
Enter a description for the property editor. It could be:
Write a nice introduction text so the visitors feel welcome" - }, - { - element: "[data-element~='overlay-property-settings'] [data-element='editor-add']", - title: "Add editor", - content: "The editor defines what data type the property is. Click Add editor to open the editor picker dialog.", - event: "click" - }, - { - element: "[data-element~='overlay-editor-picker']", - elementPreventClick: true, - title: "Editor picker", - content: "
In the editor picker dialog we can pick one of the many build in editor.
" - }, - { - element: "[data-element~='overlay-editor-picker'] [data-element='editor-Textarea']", - title: "Select editor", - content: "Select the Textarea editor which allows us to enter long texts.", - event: "click" - }, - { - element: "[data-element~='overlay-editor-settings']", - elementPreventClick: true, - title: "Editor settings", - content: "Each property editor can have individual settings. We don't want to change any of these now." - }, - { - element: "[data-element~='overlay-editor-settings'] [data-element='button-overlaySubmit']", - title: "Save editor", - content: "Click Submit to save the editor.", - event: "click" - }, - { - element: "[data-element~='overlay-property-settings'] [data-element='button-overlaySubmit']", - title: "Add property to document type", - content: "Click Submit 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 Save to create and save your new document type.", - event: "click" - } - ] - }, - { - "name": "Create Content", - "alias": "umbIntroCreateContent", - "group": "Getting Started", - "steps": [ - { - title: "Creating your first content node", - content: "The Content section contains the content of the website. Content is displayed as nodes in the content tree.
In this tour we will learn how to create our Home page for our website.
", - type: "intro" - }, - { - element: "#applications [data-element='section-content']", - title: "Navigate to the content sections", - content: "In the Content section we will find the content of our website.", - event: "click", - backdropOpacity: 0.6 - }, - { - element: "[data-element='tree-root']", - title: "Open context menu", - content: "Open the context menu by hovering the root of the content section.
Now click the three small dots to the right.
", - event: "click", - eventElement: "[data-element='tree-root'] [data-element='tree-item-options']" - }, - { - element: "[data-element='action-create-home']", - title: "Create Home page", - content: "Click on Home to create a new page of type Home.
", - event: "click" - }, - { - element: "[data-element='editor-content'] [data-element='editor-name-field']", - title: "Give your new page a name", - content: "Our new page needs a name. Enter Home in the field and click Next.
Add content to the Welcome Text field
If you don't have any ideas here is a start:
I am learning Umbraco. High Five I Rock #H5IR." - }, - { - element: "[data-element='editor-content'] [data-element='button-saveAndPublish']", - title: "Save and publish", - content: "
Now click the Save and publish button to save and publish your changes.
", - event: "click" - } - ] - }, - { - "name": "Render in template", - "alias": "umbIntroRenderInTemplate", - "group": "Getting Started", - "steps": [ - { - title: "Render your content in a template", - content: "Templating in Umbraco builds on the concept of Razor Views from asp.net MVC. - This tour is a sneak peak on how to write templates in Umbraco.
In this tour we will learn how to render content from our Home document type so we can see the content added to our Home page.
", - type: "intro" - }, - { - element: "#applications [data-element='section-settings']", - title: "Navigate to the Settings section", - content: "In the Settings section you will find all the templates
It is of course also possible to edit all your code files in your favorite code editor.
", - event: "click", - backdropOpacity: 0.6 - }, - { - element: "#tree [data-element='tree-item-templates']", - title: "Expand the Templates node", - content: "To see all our templates click the small triangle to the left of the templates node.
", - 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']", - title: "Open Home template", - content: "Click the Home template to open and edit it.
", - eventElement: "#tree [data-element='tree-item-templates'] [data-element='tree-item-Home'] a.umb-tree-item__label", - event: "click" - }, - { - element: "[data-element='editor-templates'] [data-element='code-editor']", - title: "Edit template", - content: 'The template can be edited here or in your favorite code editor.
To render the field from the document type add the following to the template:
@Model.Content.GetPropertyValue("welcomeText")'
- },
- {
- element: "[data-element='editor-templates'] [data-element='button-save']",
- title: "Save the template",
- content: "Click the Save button and your template will be saved.",
- event: "click"
- }
- ]
- },
- {
- "name": "View Home page",
- "alias": "umbIntroViewHomePage",
- "group": "Getting Started",
- "steps": [
- {
- title: "View your Umbraco site",
- content: "Our three main components to a page is done: Document type, Template, and Content - it is now time to see the result.
In this tour we will learn how to see our published website.
", - type: "intro" - }, - { - element: "#applications [data-element='section-content']", - title: "Navigate to the content sections", - content: "In the Content section we 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: "Click the Home page to open it
", - event: "click", - eventElement: "#tree [data-element='tree-item-Home'] a.umb-tree-item__label" - }, - { - element: "[data-element='editor-content'] [data-element='tab-Generic properties']", - title: "Properties", - content: "Under the properties tab you will find the default information about a content item.
", - event: "click" - }, - { - element: "[data-element='editor-content'] [data-element='property-_umb_urls']", - title: "Open page", - content: "Click the Link to document to view your page.
Tip: Click the preview button in the bottom right corner to preview changes without publishing them.
", - event: "click", - eventElement: "[data-element='editor-content'] [data-element='property-_umb_urls'] a[target='_blank']" - } - ] - }, - { - "name": "The media library", - "alias": "umbIntroMediaSection", - "group": "Getting Started", - "steps": [ - { - title: "How to use the media library", - content: "A website would be boring without media content. In Umbraco you can manage all your images, documents, videos etc. in the Media section. Here you can upload and organise your media items and see details about each item.
In this tour we will learn how to upload and orginise your Media library in Umbraco. It will also show you how to view details about a specific media item.
", - type: "intro" - }, - { - element: "#applications [data-element='section-media']", - title: "Navigate to the media section", - content: "The media section is where you will manage all your media items.", - event: "click", - backdropOpacity: 0.6 - }, - { - element: "#tree [data-element='tree-root']", - title: "Create a new folder", - content: "Let's first create a folder for our images. Hover the media root and click the three small dots on the right side of the item.
", - 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: "Select the Folder options to select the type folder.
", - event: "click" - }, - { - element: "[data-element='editor-media'] [data-element='editor-name-field']", - title: "Enter a name", - content: "Enter My folder in the field.
Click the Save button to create the new folder
", - event: "click" - }, - { - element: "[data-element='editor-media'] [data-element='dropzone']", - title: "Upload images", - content: "In the upload area you can upload your media items.
Click the Upload button and select a couple of images on your computer and upload them.
", - view: "uploadimages" - }, - { - element: "[data-element='editor-media'] [data-element='media-grid-item-0']", - title: "View media item details", - content: "Hover the media item and Click the purple bar 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: "Here you can see the image you have uploaded.
You can use the dot in the center of the image to set a focal point on the image.
" - }, - { - element: "[data-element='editor-media'] [data-element='property-umbracoBytes']", - title: "Image size", - content: "You will also find other details about the image, like the size.
You can add extra properties to an image by creating or editing the Media types
" - }, - { - element: "[data-element='editor-media'] [data-element='tab-Generic properties']", - title: "Properties", - content: "Like the content section you can also find default properties about the media item. You will find these under the properties tab.", - event: "click" - }, - { - element: "[data-element='editor-media'] [data-element='property-_umb_urls']", - title: "Link to media", - content: "The path to the media item..." - }, - { - element: "[data-element='editor-media'] [data-element='property-_umb_updatedate']", - title: "Last edited", - content: "...and information about when the media item has been created and edited." - } - ] - } - ]; return tours; } + /** + * @ngdoc method + * @name umbraco.services.tourService#startTour + * @methodOf umbraco.services.tourService + * + * @description + * Raises an event to start a tour + * @param {Object} tour The tour which should be started + */ function startTour(tour) { + validateTour(tour); eventsService.emit("appState.tour.start", tour); currentTour = tour; } + /** + * @ngdoc method + * @name umbraco.services.tourService#endTour + * @methodOf umbraco.services.tourService + * + * @description + * Raises an event to end the current tour + */ function endTour(tour) { eventsService.emit("appState.tour.end", tour); currentTour = null; @@ -448,9 +140,14 @@ } /** + * @ngdoc method + * @name umbraco.services.tourService#completeTour + * @methodOf umbraco.services.tourService + * + * @description * Completes a tour for the user, raises an event and returns a promise - * @param {any} tour - */ + * @param {Object} tour The tour which should be completed + */ function completeTour(tour) { var deferred = $q.defer(); tour.completed = true; @@ -465,24 +162,42 @@ } /** + * @ngdoc method + * @name umbraco.services.tourService#getCurrentTour + * @methodOf umbraco.services.tourService + * + * @description * Returns the current tour - */ + * @returns {Object} Returns the current tour + */ function getCurrentTour() { //TODO: This should be reset if a new user logs in return currentTour; } /** + * @ngdoc method + * @name umbraco.services.tourService#getAllTours + * @methodOf umbraco.services.tourService + * + * @description * Returns a promise of all tours with the current user statuses - */ + * @returns {Array} All registered tours + */ function getAllTours() { var tours = getTours(); return setTourStatuses(tours); } /** + * @ngdoc method + * @name umbraco.services.tourService#getGroupedTours + * @methodOf umbraco.services.tourService + * + * @description * Returns a promise of grouped tours with the current user statuses - */ + * @returns {Array} All registered tours grouped by tour group + */ function getGroupedTours() { var deferred = $q.defer(); var tours = getTours(); @@ -494,9 +209,15 @@ } /** + * @ngdoc method + * @name umbraco.services.tourService#getTourByAlias + * @methodOf umbraco.services.tourService + * + * @description * Returns a promise of the tour found by alias with the current user statuses - * @param {any} tourAlias - */ + * @param {Object} tourAlias The tour alias of the tour which should be returned + * @returns {Object} Tour object + */ function getTourByAlias(tourAlias) { var deferred = $q.defer(); var tours = getTours(); @@ -508,8 +229,14 @@ } /** + * @ngdoc method + * @name umbraco.services.tourService#getCompletedTours + * @methodOf umbraco.services.tourService + * + * @description * Returns a promise of completed tours for the user - */ + * @returns {Array} Array of completed tour aliases + */ function getCompletedTours() { var deferred = $q.defer(); currentUserResource.getTours().then(function (storedTours) { @@ -535,6 +262,43 @@ /////////// + /** + * Validates a tour object and makes sure it consists of the correct properties needed to start a tour + * @param {any} tour + */ + function validateTour(tour) { + + if (!tour) { + throw "A tour is not specified"; + } + + if (!tour.alias) { + throw "A tour alias is required"; + } + + if (!tour.steps) { + throw "Tour " + tour.alias + " is missing tour steps"; + } + + if (tour.steps && tour.steps.length === 0) { + throw "Tour " + tour.alias + " is missing tour steps"; + } + + } + + /** + * Validates a tour before it gets registered in the service + * @param {any} tour + */ + function validateTourRegistration(tour) { + // check for existing tours with the same alias + angular.forEach(tours, function (existingTour) { + if (existingTour.alias === tour.alias) { + throw "A tour with the alias " + tour.alias + " is already registered"; + } + }); + } + /** * Based on the tours given, this will set each of the tour statuses (disabled/completed) based on what is stored against the current user * @param {any} tours @@ -601,6 +365,10 @@ } var service = { + registerTour: registerTour, + registerTours: registerTours, + unregisterTour: unregisterTour, + unregisterTourGroup: unregisterTourGroup, startTour: startTour, endTour: endTour, disableTour: disableTour, @@ -613,7 +381,7 @@ //TODO: Not used getCompletedTours: getCompletedTours, //TODO: Not used - getDisabledTours: getDisabledTours, + getDisabledTours: getDisabledTours, }; return service; diff --git a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js index f187ec15cf..f5595f05d5 100644 --- a/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js +++ b/src/Umbraco.Web.UI.Client/src/views/dashboard/dashboard.tabs.controller.js @@ -37,7 +37,7 @@ function startUpDynamicContentController($timeout, dashboardResource, assetsServ // get intro tour tourService.getTourByAlias("umbIntroIntroduction").then(function (introTour) { // start intro tour if it hasn't been completed or disabled - if (introTour.disabled !== true && introTour.completed !== true) { + if (introTour && introTour.disabled !== true && introTour.completed !== true) { tourService.startTour(introTour); } });