diff --git a/www/addons/grades/services/handlers.js b/www/addons/grades/services/handlers.js index 47597bc97e6..3ed85f47c91 100644 --- a/www/addons/grades/services/handlers.js +++ b/www/addons/grades/services/handlers.js @@ -191,7 +191,7 @@ angular.module('mm.addons.grades') */ self.getActions = function(siteIds, url) { // Check it's a grade URL. - if (url.indexOf('/grade/report/user/index.php') > -1) { + if (typeof self.handles(url) != 'undefined') { var params = $mmUtil.extractUrlParams(url); if (typeof params.id != 'undefined') { var courseId = parseInt(params.id, 10); @@ -220,6 +220,19 @@ angular.module('mm.addons.grades') return []; }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/grade/report/user/index.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/messages/services/handlers.js b/www/addons/messages/services/handlers.js index de96d529b89..98139ee4de4 100644 --- a/www/addons/messages/services/handlers.js +++ b/www/addons/messages/services/handlers.js @@ -312,7 +312,7 @@ angular.module('mm.addons.messages') */ self.getActions = function(siteIds, url) { // Check it's a messages URL. - if (url.indexOf('/message/index.php') > -1) { + if (typeof self.handles(url) != 'undefined') { // Pass false because all sites should have the same siteurl. return $mmContentLinksHelper.filterSupportedSites(siteIds, isEnabledForSite, false).then(function(ids) { if (!ids.length) { @@ -364,6 +364,19 @@ angular.module('mm.addons.messages') return []; }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/message/index.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_assign/services/handlers.js b/www/addons/mod_assign/services/handlers.js index 860cc5da45f..2adb7560763 100644 --- a/www/addons/mod_assign/services/handlers.js +++ b/www/addons/mod_assign/services/handlers.js @@ -106,12 +106,25 @@ angular.module('mm.addons.mod_assign') */ self.getActions = function(siteIds, url, courseId) { // Check it's an assign URL. - if (url.indexOf('/mod/assign/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/assign/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_book/services/handlers.js b/www/addons/mod_book/services/handlers.js index 79c6208c974..ece656381dc 100644 --- a/www/addons/mod_book/services/handlers.js +++ b/www/addons/mod_book/services/handlers.js @@ -173,12 +173,25 @@ angular.module('mm.addons.mod_book') */ self.getActions = function(siteIds, url, courseId) { // Check it's a book URL. - if (url.indexOf('/mod/book/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/book/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_chat/services/handlers.js b/www/addons/mod_chat/services/handlers.js index 426feb0baa8..e5aa798b3f0 100644 --- a/www/addons/mod_chat/services/handlers.js +++ b/www/addons/mod_chat/services/handlers.js @@ -101,12 +101,25 @@ angular.module('mm.addons.mod_chat') */ self.getActions = function(siteIds, url, courseId) { // Check it's a chat URL. - if (url.indexOf('/mod/chat/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/chat/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_choice/services/handlers.js b/www/addons/mod_choice/services/handlers.js index e0baf57e4a1..ed6951d0ad4 100644 --- a/www/addons/mod_choice/services/handlers.js +++ b/www/addons/mod_choice/services/handlers.js @@ -102,12 +102,25 @@ angular.module('mm.addons.mod_choice') */ self.getActions = function(siteIds, url, courseId) { // Check it's a choice URL. - if (url.indexOf('/mod/choice/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/choice/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_folder/services/handlers.js b/www/addons/mod_folder/services/handlers.js index 472f74d7790..8fc3759e068 100644 --- a/www/addons/mod_folder/services/handlers.js +++ b/www/addons/mod_folder/services/handlers.js @@ -168,12 +168,25 @@ angular.module('mm.addons.mod_folder') */ self.getActions = function(siteIds, url, courseId) { // Check it's a folder URL. - if (url.indexOf('/mod/folder/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/folder/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_forum/services/handlers.js b/www/addons/mod_forum/services/handlers.js index 58391e98dde..a3715141513 100644 --- a/www/addons/mod_forum/services/handlers.js +++ b/www/addons/mod_forum/services/handlers.js @@ -82,7 +82,8 @@ angular.module('mm.addons.mod_forum') */ self.linksHandler = function() { - var self = {}; + var self = {}, + patterns = ['/mod/forum/view.php', '/mod/forum/discuss.php']; /** * Whether or not the handler is enabled for a certain site. @@ -122,13 +123,16 @@ angular.module('mm.addons.mod_forum') */ self.getActions = function(siteIds, url, courseId) { // Check it's a forum URL. - if (url.indexOf('/mod/forum/view.php') > -1) { + if (url.indexOf(patterns[0]) > -1) { // Forum index. return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isIndexEnabled, courseId); - } else if (url.indexOf('/mod/forum/discuss.php') > -1) { + } else if (url.indexOf(patterns[1]) > -1) { // Forum discussion. var params = $mmUtil.extractUrlParams(url); if (params.d != 'undefined') { + // If courseId is not set we check if it's set in the URL as a param. + courseId = courseId || params.courseid || params.cid; + // Pass false because all sites should have the same siteurl. return $mmContentLinksHelper.filterSupportedSites(siteIds, isDiscEnabled, false, courseId).then(function(ids) { if (!ids.length) { @@ -154,6 +158,21 @@ angular.module('mm.addons.mod_forum') return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + for (var i = 0; i < patterns.length; i++) { + var position = url.indexOf(patterns[i]); + if (position > -1) { + return url.substr(0, position); + } + } + }; + return self; }; diff --git a/www/addons/mod_glossary/services/handlers.js b/www/addons/mod_glossary/services/handlers.js index 6cf4505e0f3..dc69ed50a89 100644 --- a/www/addons/mod_glossary/services/handlers.js +++ b/www/addons/mod_glossary/services/handlers.js @@ -78,7 +78,8 @@ angular.module('mm.addons.mod_glossary') */ self.linksHandler = function() { - var self = {}; + var self = {}, + patterns = ['/mod/glossary/view.php', '/mod/glossary/showentry.php']; /** * Whether or not the handler is enabled to see glossary index for a certain site. @@ -136,6 +137,9 @@ angular.module('mm.addons.mod_glossary') function treatEntryLink(siteIds, url, courseId) { var params = $mmUtil.extractUrlParams(url); if (params.eid != 'undefined') { + // If courseId is not set we check if it's set in the URL as a param. + courseId = courseId || params.courseid || params.cid; + // Pass false because all sites should have the same siteurl. return $mmContentLinksHelper.filterSupportedSites(siteIds, isEntryEnabled, false, courseId).then(function(ids) { if (!ids.length) { @@ -183,16 +187,31 @@ angular.module('mm.addons.mod_glossary') */ self.getActions = function(siteIds, url, courseId) { // Check it's a glossary URL. - if (url.indexOf('/mod/glossary/view.php') > -1) { + if (url.indexOf(patterns[0]) > -1) { // Glossary index. return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isIndexEnabled, courseId); - } else if (url.indexOf('/mod/glossary/showentry.php') > -1) { + } else if (url.indexOf(patterns[1]) > -1) { // Glossary entry. return treatEntryLink(siteIds, url, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + for (var i = 0; i < patterns.length; i++) { + var position = url.indexOf(patterns[i]); + if (position > -1) { + return url.substr(0, position); + } + } + }; + return self; }; diff --git a/www/addons/mod_imscp/services/handlers.js b/www/addons/mod_imscp/services/handlers.js index 95f68b5c7d8..dc03d3ec548 100644 --- a/www/addons/mod_imscp/services/handlers.js +++ b/www/addons/mod_imscp/services/handlers.js @@ -177,12 +177,25 @@ angular.module('mm.addons.mod_imscp') */ self.getActions = function(siteIds, url, courseId) { // Check it's an IMSCP URL. - if (url.indexOf('/mod/imscp/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/imscp/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_label/services/handlers.js b/www/addons/mod_label/services/handlers.js index 16282cb19f4..433635e4ebc 100644 --- a/www/addons/mod_label/services/handlers.js +++ b/www/addons/mod_label/services/handlers.js @@ -111,12 +111,25 @@ angular.module('mm.addons.mod_label') */ self.getActions = function(siteIds, url, courseId) { // Check it's a label URL. - if (url.indexOf('/mod/label/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/label/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_lti/services/handlers.js b/www/addons/mod_lti/services/handlers.js index 7e43b5315a6..02f534f2af7 100644 --- a/www/addons/mod_lti/services/handlers.js +++ b/www/addons/mod_lti/services/handlers.js @@ -152,12 +152,25 @@ angular.module('mm.addons.mod_lti') */ self.getActions = function(siteIds, url, courseId) { // Check it's a LTI URL. - if (url.indexOf('/mod/lti/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/lti/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_page/services/handlers.js b/www/addons/mod_page/services/handlers.js index 4d9244b09fd..ccc5a3e71f9 100644 --- a/www/addons/mod_page/services/handlers.js +++ b/www/addons/mod_page/services/handlers.js @@ -171,12 +171,25 @@ angular.module('mm.addons.mod_page') */ self.getActions = function(siteIds, url, courseId) { // Check it's a page URL. - if (url.indexOf('/mod/page/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/page/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_resource/services/handlers.js b/www/addons/mod_resource/services/handlers.js index 05a50263871..4cc33dab696 100644 --- a/www/addons/mod_resource/services/handlers.js +++ b/www/addons/mod_resource/services/handlers.js @@ -183,12 +183,25 @@ angular.module('mm.addons.mod_resource') */ self.getActions = function(siteIds, url, courseId) { // Check it's a resource URL. - if (url.indexOf('/mod/resource/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/resource/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_scorm/services/handlers.js b/www/addons/mod_scorm/services/handlers.js index ebeee392b9f..6440b22eb46 100644 --- a/www/addons/mod_scorm/services/handlers.js +++ b/www/addons/mod_scorm/services/handlers.js @@ -186,12 +186,25 @@ angular.module('mm.addons.mod_scorm') */ self.getActions = function(siteIds, url, courseId) { // Check it's a SCORM URL. - if (url.indexOf('/mod/scorm/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/scorm/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_survey/services/handlers.js b/www/addons/mod_survey/services/handlers.js index 0d98c8656a4..37264b013d6 100644 --- a/www/addons/mod_survey/services/handlers.js +++ b/www/addons/mod_survey/services/handlers.js @@ -106,12 +106,25 @@ angular.module('mm.addons.mod_survey') */ self.getActions = function(siteIds, url, courseId) { // Check it's a survey URL. - if (url.indexOf('/mod/survey/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/survey/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/mod_url/services/handlers.js b/www/addons/mod_url/services/handlers.js index c0ea2e96933..0447df15331 100644 --- a/www/addons/mod_url/services/handlers.js +++ b/www/addons/mod_url/services/handlers.js @@ -121,12 +121,25 @@ angular.module('mm.addons.mod_url') */ self.getActions = function(siteIds, url, courseId) { // Check it's a mod_url URL. - if (url.indexOf('/mod/url/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); } return $q.when([]); }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + var position = url.indexOf('/mod/url/view.php'); + if (position > -1) { + return url.substr(0, position); + } + }; + return self; }; diff --git a/www/addons/participants/services/handlers.js b/www/addons/participants/services/handlers.js index 377ca066335..19a2c786c68 100644 --- a/www/addons/participants/services/handlers.js +++ b/www/addons/participants/services/handlers.js @@ -21,7 +21,7 @@ angular.module('mm.addons.participants') * @ngdoc service * @name $mmaParticipantsHandlers */ -.factory('$mmaParticipantsHandlers', function($mmaParticipants, mmCoursesAccessMethods, $mmUtil, $mmContentLinksHelper) { +.factory('$mmaParticipantsHandlers', function($mmaParticipants, mmCoursesAccessMethods, $mmUtil, $state) { var self = {}; /** @@ -102,7 +102,7 @@ angular.module('mm.addons.participants') */ self.getActions = function(siteIds, url) { // Check it's a user URL. - if (url.indexOf('grade/report/user') == -1 && url.indexOf('/user/index.php') > -1) { + if (typeof self.handles(url) != 'undefined') { var params = $mmUtil.extractUrlParams(url); if (typeof params.id != 'undefined') { // Return actions. @@ -111,10 +111,14 @@ angular.module('mm.addons.participants') icon: 'ion-eye', sites: siteIds, action: function(siteId) { - var stateParams = { - course: {id: parseInt(params.id, 10)} - }; - $mmContentLinksHelper.goInSite('site.participants', stateParams, siteId); + // Use redirect to make the participants list the new history root (to avoid "loops" in history). + $state.go('redirect', { + siteid: siteId, + state: 'site.participants', + params: { + course: {id: parseInt(params.id, 10)} + } + }); } }]; } @@ -122,6 +126,22 @@ angular.module('mm.addons.participants') return []; }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + // Verify it's not a grade URL. + if (url.indexOf('grade/report/user') == -1) { + var position = url.indexOf('/user/index.php'); + if (position > -1) { + return url.substr(0, position); + } + } + }; + return self; }; diff --git a/www/config.json b/www/config.json index ed0b4383085..f515ef47f52 100644 --- a/www/config.json +++ b/www/config.json @@ -8,5 +8,6 @@ "wsservice" : "moodle_mobile_app", "wsextservice" : "local_mobile", "demo_sites": {"student": {"url": "http://school.demo.moodle.net", "username": "student", "password": "moodle"}, "teacher": {"url": "http://school.demo.moodle.net", "username": "teacher", "password": "moodle"}, "cva": {"url": "http://mm.cvaconsulting.com/moodle", "username": "student", "password": "student"}}, - "gcmpn": "694767596569" + "gcmpn": "694767596569", + "customurlscheme": "moodlemobile" } diff --git a/www/core/components/contentlinks/main.js b/www/core/components/contentlinks/main.js index 13ed3879859..8dab7ace03d 100644 --- a/www/core/components/contentlinks/main.js +++ b/www/core/components/contentlinks/main.js @@ -33,4 +33,11 @@ angular.module('mm.core.contentlinks', []) url: null } }); +}) + +.run(function($log, $mmURLDelegate, $mmContentLinksHelper) { + + $log = $log.getInstance('mmContentLinks'); + + $mmURLDelegate.register('mmContentLinks', $mmContentLinksHelper.handleCustomUrl); }); diff --git a/www/core/components/contentlinks/services/delegate.js b/www/core/components/contentlinks/services/delegate.js index 2c7f970ba9a..a354ff75091 100644 --- a/www/core/components/contentlinks/services/delegate.js +++ b/www/core/components/contentlinks/services/delegate.js @@ -40,6 +40,7 @@ angular.module('mm.core.contentlinks') * - icon: Icon related to the action to do. * - sites: Sites IDs that support the action. Subset of 'siteIds'. * - action(siteId): A function to be called when the link is clicked. + * - handles(url) (String) Check if a URL is handled by this handler. If so, returns the site URL. * @param {Number} [priority] Handler's priority. */ self.registerLinkHandler = function(name, handler, priority) { @@ -72,15 +73,16 @@ angular.module('mm.core.contentlinks') * @param {String} url URL to handle. * @param {Number} [courseId] Course ID related to the URL. Optional but recommended since some handlers might require * to know the courseid if Moodle version is previous to 3.0. + * @param {String} [username] Username to use to filter sites. * @return {Promise} Promise resolved with the actions. See {@link $mmContentLinksDelegate#registerLinkHandler}. */ - self.getActionsFor = function(url, courseId) { + self.getActionsFor = function(url, courseId, username) { if (!url) { return $q.when([]); } // Get the list of sites the URL belongs to. - return $mmSitesManager.getSiteIdsFromUrl(url, true).then(function(siteIds) { + return $mmSitesManager.getSiteIdsFromUrl(url, true, username).then(function(siteIds) { var linkActions = [], promises = []; @@ -108,6 +110,36 @@ angular.module('mm.core.contentlinks') }); }; + /** + * Get the site URL if the URL is supported by any handler. + * + * @module mm.core.contentlinks + * @ngdoc method + * @name $mmContentLinksDelegate#getSiteUrl + * @param {String} url URL to handle. + * @return {String} Site URL if the URL is supported by any handler, undefined otherwise. + */ + self.getSiteUrl = function(url) { + if (!url) { + return; + } + + // Check if any handler supports this URL. + for (var name in linkHandlers) { + var handler = linkHandlers[name]; + if (typeof handler.instance === 'undefined') { + handler.instance = $mmUtil.resolveObject(handler.handler, true); + } + + if (handler.instance && handler.instance.handles) { + var siteUrl = handler.instance.handles(url); + if (siteUrl) { + return siteUrl; + } + } + } + }; + /** * Sort actions by priority. Each object in the actions param must have a priority and a list of actions. * The returned array only contains the actions ordered by priority. diff --git a/www/core/components/contentlinks/services/helper.js b/www/core/components/contentlinks/services/helper.js index 5f56e4aa091..07a0cb17c4e 100644 --- a/www/core/components/contentlinks/services/helper.js +++ b/www/core/components/contentlinks/services/helper.js @@ -22,7 +22,7 @@ angular.module('mm.core.contentlinks') * @name $mmContentLinksHelper */ .factory('$mmContentLinksHelper', function($log, $ionicHistory, $state, $mmSite, $mmContentLinksDelegate, $mmUtil, $translate, - $mmCourseHelper) { + $mmCourseHelper, $mmSitesManager, $q, $mmLoginHelper, $mmText, mmCoreConfigConstants) { $log = $log.getInstance('$mmContentLinksHelper'); @@ -131,21 +131,128 @@ angular.module('mm.core.contentlinks') return $state.go('mm_contentlinks.choosesite', {url: url}); }; + /** + * Handle a URL received by Custom URL Scheme. + * + * @module mm.core.contentlinks + * @ngdoc method + * @name $mmContentLinksHelper#handleCustomUrl + * @param {String} url URL to handle. + * @return {True} True if the URL should be handled by this component, false otherwise. + */ + self.handleCustomUrl = function(url) { + var contentLinksScheme = mmCoreConfigConstants.customurlscheme + '://link='; + if (url.indexOf(contentLinksScheme) == -1) { + return false; + } + + // App opened using custom URL scheme. + $log.debug('Treating custom URL scheme: ' + url); + + var modal = $mmUtil.showModalLoading(), + username; + + // Delete the scheme from the URL. + url = url.replace(contentLinksScheme, ''); + + // Detect if there's a user specified. + username = $mmText.getUsernameFromUrl(url); + if (username) { + url = url.replace(username + '@', ''); // Remove the username from the URL. + } + + // Check if the site is stored. + $mmSitesManager.getSiteIdsFromUrl(url, false, username).then(function(siteIds) { + if (siteIds.length) { + modal.dismiss(); // Dismiss modal so it doesn't collide with confirms. + return self.handleLink(url, username).then(function(treated) { + if (!treated) { + $mmUtil.showErrorModal('mm.contentlinks.errornoactions', true); + } + }); + } else { + // Get the site URL. + var siteUrl = $mmContentLinksDelegate.getSiteUrl(url), + formatted = $mmUtil.formatURL(siteUrl); + if (!siteUrl || !$mmUtil.isValidURL(formatted)) { + $mmUtil.showErrorModal('mm.login.invalidsite', true); + return; + } + + // Check that site exists. + return $mmSitesManager.checkSite(siteUrl).then(function(result) { + // Site exists. We'll allow to add it. + var promise, + ssoNeeded = $mmLoginHelper.isSSOLoginNeeded(result.code); + + modal.dismiss(); // Dismiss modal so it doesn't collide with confirms. + + if (!$mmSite.isLoggedIn()) { + if (ssoNeeded) { + // Ask SSO confirmation. + promise = $mmUtil.showConfirm($translate('mm.login.logininsiterequired')); + } else { + // Not logged in and no SSO, no need to confirm. + promise = $q.when(); + } + } else { + // Ask the user before changing site. + promise = $mmUtil.showConfirm($translate('mm.contentlinks.confirmurlothersite')).then(function() { + if (!ssoNeeded) { + return $mmSitesManager.logout().catch(function() { + // Ignore errors (shouldn't happen). + }); + } + }); + } + + return promise.then(function() { + if (ssoNeeded) { + $mmLoginHelper.openBrowserForSSOLogin(result.siteurl); + } else { + $state.go('mm_login.credentials', { + siteurl: result.siteurl, + username: username, + urltoopen: url + }); + } + }); + + }, function(error) { + $mmUtil.showErrorModal(error); + }); + } + }).finally(function() { + modal.dismiss(); + }); + + return true; + }; + /** * Handle a link. * * @module mm.core.contentlinks * @ngdoc method * @name $mmContentLinksHelper#handleLink - * @param {String} url URL to handle. - * @return {Promise} Promise resolved with a boolean: true if URL was treated, false otherwise. + * @param {String} url URL to handle. + * @param {String} [username] Username related with the URL. E.g. in 'http://myuser@m.com', url would be 'http://m.com' and + * the username 'myuser'. Don't use it if you don't want to filter by username. + * @return {Promise} Promise resolved with a boolean: true if URL was treated, false otherwise. */ - self.handleLink = function(url) { + self.handleLink = function(url, username) { // Check if the link should be treated by some component/addon. - return $mmContentLinksDelegate.getActionsFor(url).then(function(actions) { + return $mmContentLinksDelegate.getActionsFor(url, undefined, username).then(function(actions) { var action = self.getFirstValidAction(actions); if (action) { - if (action.sites.length == 1 && action.sites[0] == $mmSite.getId()) { + if (!$mmSite.isLoggedIn()) { + // No current site. Perform the action if only 1 site found, choose the site otherwise. + if (action.sites.length == 1) { + action.action(action.sites[0]); + } else { + self.goToChooseSite(url); + } + } else if (action.sites.length == 1 && action.sites[0] == $mmSite.getId()) { // Current site. action.action(action.sites[0]); } else { @@ -160,6 +267,7 @@ angular.module('mm.core.contentlinks') } return true; } + return false; }).catch(function() { return false; }); @@ -180,6 +288,9 @@ angular.module('mm.core.contentlinks') self.treatModuleIndexUrl = function(siteIds, url, isEnabled, courseId) { var params = $mmUtil.extractUrlParams(url); if (typeof params.id != 'undefined') { + // If courseId is not set we check if it's set in the URL as a param. + courseId = courseId || params.courseid || params.cid; + // Pass false because all sites should have the same siteurl. return self.filterSupportedSites(siteIds, isEnabled, false, courseId).then(function(ids) { if (!ids.length) { diff --git a/www/core/components/course/services/course.js b/www/core/components/course/services/course.js index cbbf61878b9..b9194f73901 100644 --- a/www/core/components/course/services/course.js +++ b/www/core/components/course/services/course.js @@ -360,13 +360,14 @@ angular.module('mm.core.course') // Get all the sections in the course and iterate over them to find it. return self.getSections(courseId, {}, siteId).then(function(sections) { - sections.forEach(function(section) { - section.modules.forEach(function(module) { - if (module.id == moduleId) { + for (var i = 0, seclen = sections.length; i < seclen; i++) { + var section = sections[i]; + for (var j = 0, modlen = section.modules.length; j < modlen; j++) { + if (section.modules[j].id == moduleId) { return section.id; } - }); - }); + } + } // Not found. return $q.reject(); }); diff --git a/www/core/components/courses/services/handlers.js b/www/core/components/courses/services/handlers.js index e26e7dddf29..dcb3305e8dc 100644 --- a/www/core/components/courses/services/handlers.js +++ b/www/core/components/courses/services/handlers.js @@ -21,7 +21,7 @@ angular.module('mm.core.courses') * @ngdoc service * @name $mmCoursesHandlers */ -.factory('$mmCoursesHandlers', function($mmSite, $state, $mmCourses, $q, $mmUtil, $translate, $timeout, $mmContentLinksHelper, +.factory('$mmCoursesHandlers', function($mmSite, $state, $mmCourses, $q, $mmUtil, $translate, $timeout, mmCoursesEnrolInvalidKey) { var self = {}; @@ -74,7 +74,12 @@ angular.module('mm.core.courses') }); }).then(function() { modal.dismiss(); - $state.go('site.mm_course', {courseid: parseInt(courseId)}); + // Use redirect to make the course the new history root (to avoid "loops" in history). + $state.go('redirect', { + siteid: $mmSite.getId(), + state: 'site.mm_course', + params: {courseid: courseId} + }); }); } @@ -160,8 +165,7 @@ angular.module('mm.core.courses') */ self.getActions = function(siteIds, url) { // Check if it's a course URL. - if (url.indexOf('enrol/index.php') > -1 || url.indexOf('course/enrol.php') > -1 || - url.indexOf('course/view.php') > -1) { + if (typeof self.handles(url) != 'undefined') { var params = $mmUtil.extractUrlParams(url); if (typeof params.id != 'undefined') { // Return actions. @@ -174,7 +178,12 @@ angular.module('mm.core.courses') if (siteId == $mmSite.getId()) { actionEnrol(parseInt(params.id, 10), url); } else { - $mmContentLinksHelper.goInSite('site.mm_course', {courseid: parseInt(params.id, 10)}, siteId); + // Use redirect to make the course the new history root (to avoid "loops" in history). + $state.go('redirect', { + siteid: siteId, + state: 'site.mm_course', + params: {courseid: parseInt(params.id, 10)} + }); } } }]; @@ -183,6 +192,23 @@ angular.module('mm.core.courses') return []; }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + // Accept any of these patterns. + var patterns = ['/enrol/index.php', '/course/enrol.php', '/course/view.php']; + for (var i = 0; i < patterns.length; i++) { + var position = url.indexOf(patterns[i]); + if (position > -1) { + return url.substr(0, position); + } + } + }; + return self; }; diff --git a/www/core/components/login/controllers/credentials.js b/www/core/components/login/controllers/credentials.js index fc6be6637c7..6e8b99dd1b7 100644 --- a/www/core/components/login/controllers/credentials.js +++ b/www/core/components/login/controllers/credentials.js @@ -22,12 +22,15 @@ angular.module('mm.core.login') * @name mmLoginCredentialsCtrl */ .controller('mmLoginCredentialsCtrl', function($scope, $state, $stateParams, $mmSitesManager, $mmUtil, $ionicHistory, $mmApp, - $q, $mmLoginHelper, $translate) { + $q, $mmLoginHelper, $translate, $mmContentLinksDelegate, $mmContentLinksHelper) { $scope.siteurl = $stateParams.siteurl; - $scope.credentials = {}; + $scope.credentials = { + username: $stateParams.username + }; - var siteChecked = false; + var siteChecked = false, + urlToOpen = $stateParams.urltoopen; // Function to check if a site uses local_mobile, requires SSO login, etc. // This should be used only if a fixed URL is set, otherwise this check is already performed in mmLoginSiteCtrl. @@ -105,7 +108,21 @@ angular.module('mm.core.login') return $mmSitesManager.newSite(data.siteurl, data.token).then(function() { delete $scope.credentials; // Delete username and password from the scope. $ionicHistory.nextViewOptions({disableBack: true}); - $state.go('site.mm_courses'); + + if (urlToOpen) { + // There's a content link to open. + return $mmContentLinksDelegate.getActionsFor(urlToOpen, undefined, username).then(function(actions) { + action = $mmContentLinksHelper.getFirstValidAction(actions); + if (action && action.sites.length) { + // Action should only have 1 site because we're filtering by username. + action.action(action.sites[0]); + } else { + $state.go('site.mm_courses'); + } + }); + } else { + $state.go('site.mm_courses'); + } }); }).catch(function(error) { $mmUtil.showErrorModal(error); diff --git a/www/core/components/login/main.js b/www/core/components/login/main.js index 404742d0fc9..10c5bade654 100644 --- a/www/core/components/login/main.js +++ b/www/core/components/login/main.js @@ -59,7 +59,9 @@ angular.module('mm.core.login', []) templateUrl: 'core/components/login/templates/credentials.html', controller: 'mmLoginCredentialsCtrl', params: { - siteurl: '' + siteurl: '', + username: '', + urltoopen: '' // For content links. }, onEnter: function($state, $stateParams) { // Do not allow access to this page when the URL was not passed. diff --git a/www/core/components/user/services/handlers.js b/www/core/components/user/services/handlers.js index b33b859ad12..8b7122b7432 100644 --- a/www/core/components/user/services/handlers.js +++ b/www/core/components/user/services/handlers.js @@ -67,6 +67,26 @@ angular.module('mm.core.user') return []; }; + /** + * Check if the URL is handled by this handler. If so, returns the URL of the site. + * + * @param {String} url URL to check. + * @return {String} Site URL. Undefined if the URL doesn't belong to this handler. + */ + self.handles = function(url) { + // Accept any of these patterns. + var patterns = ['/user/view.php', '/user/profile.php']; + // Verify it's not a grade URL. + if (url.indexOf('grade/report/user') == -1) { + for (var i = 0; i < patterns.length; i++) { + var position = url.indexOf(patterns[i]); + if (position > -1) { + return url.substr(0, position); + } + } + } + }; + return self; }; diff --git a/www/core/lib/sitesmanager.js b/www/core/lib/sitesmanager.js index 0e0fee231fd..1e84a0ee730 100644 --- a/www/core/lib/sitesmanager.js +++ b/www/core/lib/sitesmanager.js @@ -677,12 +677,15 @@ angular.module('mm.core') * @param {String} url URL to check. * @param {Boolean} prioritize True if it should prioritize current site. If the URL belongs to current site then it won't * check any other site, it will only return current site. + * @param {String} [username] If set, it will return only the sites where the current user has this username. * @return {Promise} Promise resolved with the site IDs (array). */ - self.getSiteIdsFromUrl = function(url, prioritize) { + self.getSiteIdsFromUrl = function(url, prioritize, username) { // Check current site first, it has priority over the rest of sites. if (prioritize && currentSite && currentSite.containsUrl(url)) { - return $q.when([currentSite.getId()]); + if (!username || currentSite.getInfo().username == username) { + return $q.when([currentSite.getId()]); + } } // Check if URL has http(s) protocol. @@ -708,7 +711,9 @@ angular.module('mm.core') sites[site.id] = $mmSitesFactory.makeSite(site.id, site.siteurl, site.token, site.infos); } if (sites[site.id].containsUrl(url)) { - ids.push(site.id); + if (!username || sites[site.id].getInfo().username == username) { + ids.push(site.id); + } } }); return ids; diff --git a/www/core/lib/text.js b/www/core/lib/text.js index 9e7a52fbcb2..9de70e97320 100644 --- a/www/core/lib/text.js +++ b/www/core/lib/text.js @@ -243,5 +243,27 @@ angular.module('mm.core') return url; }; + /** + * Gets a username from a URL like: user@mysite.com. + * + * @module mm.core + * @ngdoc method + * @name $mmText#getUsernameFromUrl + * @param {String} url URL to treat. + * @return {String} Username. Undefined if no username found. + */ + self.getUsernameFromUrl = function(url) { + if (url.indexOf('@') > -1) { + // Get URL without protocol. + var withoutProtocol = url.replace(/.*?:\/\//, ''), + matches = withoutProtocol.match(/[^@]*/); + + // Make sure that @ is at the start of the URL, not in a param at the end. + if (matches && matches.length && !matches[0].match(/[\/|?]/)) { + return matches[0]; + } + } + }; + return self; });