{{getDivider(entries[$index])}}
{{entry.concept}}
diff --git a/www/addons/mod_imscp/main.js b/www/addons/mod_imscp/main.js index a66678e8ee1..10a51ceeec8 100644 --- a/www/addons/mod_imscp/main.js +++ b/www/addons/mod_imscp/main.js @@ -36,7 +36,8 @@ angular.module('mm.addons.mod_imscp', ['mm.core']) }) -.config(function($mmCourseDelegateProvider, $mmCoursePrefetchDelegateProvider) { - $mmCourseDelegateProvider.registerContentHandler('mmaModImscp', 'imscp', '$mmaModImscpCourseContentHandler'); +.config(function($mmCourseDelegateProvider, $mmCoursePrefetchDelegateProvider, $mmContentLinksDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModImscp', 'imscp', '$mmaModImscpHandlers.courseContent'); $mmCoursePrefetchDelegateProvider.registerPrefetchHandler('mmaModImscp', 'imscp', '$mmaModImscpPrefetchHandler'); + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModImscp', '$mmaModImscpHandlers.linksHandler'); }); diff --git a/www/addons/mod_imscp/services/course_content_handler.js b/www/addons/mod_imscp/services/course_content_handler.js deleted file mode 100644 index 11de15fcbbe..00000000000 --- a/www/addons/mod_imscp/services/course_content_handler.js +++ /dev/null @@ -1,128 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -angular.module('mm.addons.mod_imscp') - -/** - * Mod IMSCP course content handler. - * - * @module mm.addons.mod_imscp - * @ngdoc service - * @name $mmaModImscpCourseContentHandler - */ -.factory('$mmaModImscpCourseContentHandler', function($mmCourse, $mmaModImscp, $mmEvents, $state, $mmSite, $mmUtil, $mmFilepool, - $mmCoursePrefetchDelegate, mmCoreDownloading, mmCoreNotDownloaded, mmCoreOutdated, mmCoreEventPackageStatusChanged, - mmaModImscpComponent) { - - var self = {}; - - /** - * Whether or not the module is enabled for the site. - * - * @module mm.addons.mod_imscp - * @ngdoc method - * @name $mmaModImscpCourseContentHandler#isEnabled - * @return {Boolean} - */ - self.isEnabled = function() { - return $mmaModImscp.isPluginEnabled(); - }; - - /** - * Get the controller. - * - * @module mm.addons.mod_imscp - * @ngdoc method - * @name $mmaModImscpCourseContentHandler#getController - * @param {Object} module The module info. - * @param {Number} courseid The course ID. - * @return {Function} - */ - self.getController = function(module, courseid) { - return function($scope) { - var downloadBtn, - refreshBtn, - revision = $mmFilepool.getRevisionFromFileList(module.contents), - timemodified = $mmFilepool.getTimemodifiedFromFileList(module.contents); - - downloadBtn = { - hidden: true, - icon: 'ion-ios-cloud-download-outline', - label: 'mm.core.download', - action: function(e) { - e.preventDefault(); - e.stopPropagation(); - $mmaModImscp.prefetchContent(module).catch(function() { - if (!$scope.$$destroyed) { - $mmUtil.showErrorModal('mm.core.errordownloading', true); - } - }); - } - }; - - refreshBtn = { - icon: 'ion-android-refresh', - label: 'mm.core.refresh', - hidden: true, - action: function(e) { - e.preventDefault(); - e.stopPropagation(); - $mmaModImscp.invalidateContent(module.id).then(function() { - $mmaModImscp.prefetchContent(module).catch(function() { - if (!$scope.$$destroyed) { - $mmUtil.showErrorModal('mm.core.errordownloading', true); - } - }); - }); - } - }; - - $scope.title = module.name; - $scope.icon = $mmCourse.getModuleIconSrc('imscp'); - $scope.buttons = [downloadBtn, refreshBtn]; - $scope.spinner = false; - - $scope.action = function(e) { - e.preventDefault(); - e.stopPropagation(); - $state.go('site.mod_imscp', {module: module, courseid: courseid}); - }; - - // Show buttons according to module status. - function showStatus(status) { - if (status) { - $scope.spinner = status === mmCoreDownloading; - downloadBtn.hidden = status !== mmCoreNotDownloaded; - refreshBtn.hidden = status !== mmCoreOutdated; - } - } - - // Listen for changes on this module status. - var statusObserver = $mmEvents.on(mmCoreEventPackageStatusChanged, function(data) { - if (data.siteid === $mmSite.getId() && data.componentId === module.id && data.component === mmaModImscpComponent) { - showStatus(data.status); - } - }); - - // Get current status to decide which icon should be shown. - $mmCoursePrefetchDelegate.getModuleStatus(module, courseid, revision, timemodified).then(showStatus); - - $scope.$on('$destroy', function() { - statusObserver && statusObserver.off && statusObserver.off(); - }); - }; - }; - - return self; -}); diff --git a/www/addons/mod_imscp/services/handlers.js b/www/addons/mod_imscp/services/handlers.js new file mode 100644 index 00000000000..95f68b5c7d8 --- /dev/null +++ b/www/addons/mod_imscp/services/handlers.js @@ -0,0 +1,190 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +angular.module('mm.addons.mod_imscp') + +/** + * Mod IMSCP handlers. + * + * @module mm.addons.mod_imscp + * @ngdoc service + * @name $mmaModImscpHandlers + */ +.factory('$mmaModImscpHandlers', function($mmCourse, $mmaModImscp, $mmEvents, $state, $mmSite, $mmUtil, $mmFilepool, + $mmCoursePrefetchDelegate, mmCoreDownloading, mmCoreNotDownloaded, mmCoreOutdated, mmCoreEventPackageStatusChanged, + mmaModImscpComponent, $mmContentLinksHelper, $q) { + var self = {}; + + /** + * Course content handler. + * + * @module mm.addons.mod_imscp + * @ngdoc method + * @name $mmaModImscpHandlers#courseContent + */ + self.courseContent = function() { + + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @module mm.addons.mod_imscp + * @ngdoc method + * @name $mmaModImscpCourseContentHandler#isEnabled + * @return {Boolean} + */ + self.isEnabled = function() { + return $mmaModImscp.isPluginEnabled(); + }; + + /** + * Get the controller. + * + * @module mm.addons.mod_imscp + * @ngdoc method + * @name $mmaModImscpCourseContentHandler#getController + * @param {Object} module The module info. + * @param {Number} courseid The course ID. + * @return {Function} + */ + self.getController = function(module, courseid) { + return function($scope) { + var downloadBtn, + refreshBtn, + revision = $mmFilepool.getRevisionFromFileList(module.contents), + timemodified = $mmFilepool.getTimemodifiedFromFileList(module.contents); + + downloadBtn = { + hidden: true, + icon: 'ion-ios-cloud-download-outline', + label: 'mm.core.download', + action: function(e) { + e.preventDefault(); + e.stopPropagation(); + $mmaModImscp.prefetchContent(module).catch(function() { + if (!$scope.$$destroyed) { + $mmUtil.showErrorModal('mm.core.errordownloading', true); + } + }); + } + }; + + refreshBtn = { + icon: 'ion-android-refresh', + label: 'mm.core.refresh', + hidden: true, + action: function(e) { + e.preventDefault(); + e.stopPropagation(); + $mmaModImscp.invalidateContent(module.id).then(function() { + $mmaModImscp.prefetchContent(module).catch(function() { + if (!$scope.$$destroyed) { + $mmUtil.showErrorModal('mm.core.errordownloading', true); + } + }); + }); + } + }; + + $scope.title = module.name; + $scope.icon = $mmCourse.getModuleIconSrc('imscp'); + $scope.buttons = [downloadBtn, refreshBtn]; + $scope.spinner = false; + + $scope.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $state.go('site.mod_imscp', {module: module, courseid: courseid}); + }; + + // Show buttons according to module status. + function showStatus(status) { + if (status) { + $scope.spinner = status === mmCoreDownloading; + downloadBtn.hidden = status !== mmCoreNotDownloaded; + refreshBtn.hidden = status !== mmCoreOutdated; + } + } + + // Listen for changes on this module status. + var statusObserver = $mmEvents.on(mmCoreEventPackageStatusChanged, function(data) { + if (data.siteid === $mmSite.getId() && data.componentId === module.id && data.component === mmaModImscpComponent) { + showStatus(data.status); + } + }); + + // Get current status to decide which icon should be shown. + $mmCoursePrefetchDelegate.getModuleStatus(module, courseid, revision, timemodified).then(showStatus); + + $scope.$on('$destroy', function() { + statusObserver && statusObserver.off && statusObserver.off(); + }); + }; + }; + + return self; + }; + + /** + * Content links handler. + * + * @module mm.addons.mod_imscp + * @ngdoc method + * @name $mmaModImscpHandlers#linksHandler + */ + self.linksHandler = function() { + + var self = {}; + + /** + * Whether or not the handler is enabled for a certain site. + * + * @param {String} siteId Site ID. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with true if enabled. + */ + function isEnabled(siteId, courseId) { + return $mmaModImscp.isPluginEnabled(siteId).then(function(enabled) { + if (!enabled) { + return false; + } + return courseId || $mmCourse.canGetModuleWithoutCourseId(siteId); + }); + } + + /** + * Get actions to perform with the link. + * + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with the list of actions. + * See {@link $mmContentLinksDelegate#registerLinkHandler}. + */ + self.getActions = function(siteIds, url, courseId) { + // Check it's an IMSCP URL. + if (url.indexOf('/mod/imscp/view.php') > -1) { + return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); + } + return $q.when([]); + }; + + return self; + }; + + return self; +}); diff --git a/www/addons/mod_imscp/services/imscp.js b/www/addons/mod_imscp/services/imscp.js index 93a92b179be..4c564d82ecb 100644 --- a/www/addons/mod_imscp/services/imscp.js +++ b/www/addons/mod_imscp/services/imscp.js @@ -21,7 +21,7 @@ angular.module('mm.addons.mod_imscp') * @ngdoc service * @name $mmaModImscp */ -.factory('$mmaModImscp', function($mmFilepool, $mmSite, $mmFS, $log, $q, $sce, $mmApp, mmaModImscpComponent) { +.factory('$mmaModImscp', function($mmFilepool, $mmSite, $mmFS, $log, $q, $sce, $mmApp, $mmSitesManager, mmaModImscpComponent) { $log = $log.getInstance('$mmaModImscp'); var self = {}, @@ -339,17 +339,22 @@ angular.module('mm.addons.mod_imscp') }; /** - * Return whether or not the plugin is enabled. + * Return whether or not the plugin is enabled in a certain site. * * @module mm.addons.mod_imscp * @ngdoc method * @name $mmaModImscp#isPluginEnabled - * @return {Boolean} True if plugin is enabled, false otherwise. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. */ - self.isPluginEnabled = function() { - var version = $mmSite.getInfo().version; - // Require Moodle 2.9. - return version && (parseInt(version) >= 2015051100) && $mmSite.canDownloadFiles(); + self.isPluginEnabled = function(siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + var version = site.getInfo().version; + // Require Moodle 2.9. + return version && (parseInt(version) >= 2015051100) && site.canDownloadFiles(); + }); }; /** diff --git a/www/addons/mod_label/main.js b/www/addons/mod_label/main.js index 045d9ae4bcf..34cf7ce72f8 100644 --- a/www/addons/mod_label/main.js +++ b/www/addons/mod_label/main.js @@ -32,6 +32,7 @@ angular.module('mm.addons.mod_label', ['mm.core']) }) -.config(function($mmCourseDelegateProvider) { - $mmCourseDelegateProvider.registerContentHandler('mmaModLabel', 'label', '$mmaModLabelCourseContentHandler'); +.config(function($mmCourseDelegateProvider, $mmContentLinksDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModLabel', 'label', '$mmaModLabelHandlers.courseContent'); + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModLabel', '$mmaModLabelHandlers.linksHandler'); }); diff --git a/www/addons/mod_label/services/course_content_handler.js b/www/addons/mod_label/services/course_content_handler.js deleted file mode 100644 index 034b98105b4..00000000000 --- a/www/addons/mod_label/services/course_content_handler.js +++ /dev/null @@ -1,67 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -angular.module('mm.addons.mod_label') - -/** - * Mod label course content handler. - * - * @module mm.addons.mod_label - * @ngdoc service - * @name $mmaModLabelCourseContentHandler - */ -.factory('$mmaModLabelCourseContentHandler', function($mmText, $translate, $state) { - var self = {}; - - /** - * Whether or not the module is enabled for the site. - * - * @module mm.addons.mod_label - * @ngdoc method - * @name $mmaModLabelCourseContentHandler#isEnabled - * @return {Boolean} - */ - self.isEnabled = function() { - return true; - }; - - /** - * Get the controller. - * - * @module mm.addons.mod_label - * @ngdoc method - * @name $mmaModLabelCourseContentHandler#isEnabled - * @param {Object} module The module info. - * @return {Function} - */ - self.getController = function(module) { - return function($scope) { - var title = $mmText.shortenText($mmText.cleanTags(module.description).trim(), 128); - if (title.length <= 0) { - $translate('mma.mod_label.taptoview').then(function(taptoview) { - $scope.title = '' + taptoview + ''; - }); - } else { - $scope.title = title; - } - - $scope.icon = false; - $scope.action = function(e) { - $state.go('site.mod_label', {description: module.description}); - }; - }; - }; - - return self; -}); diff --git a/www/addons/mod_label/services/handlers.js b/www/addons/mod_label/services/handlers.js new file mode 100644 index 00000000000..16282cb19f4 --- /dev/null +++ b/www/addons/mod_label/services/handlers.js @@ -0,0 +1,124 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +angular.module('mm.addons.mod_label') + +/** + * Mod label handlers. + * + * @module mm.addons.mod_label + * @ngdoc service + * @name $mmaModLabelHandlers + */ +.factory('$mmaModLabelHandlers', function($mmText, $translate, $state, $mmContentLinksHelper, $q, $mmCourse) { + var self = {}; + + /** + * Course content handler. + * + * @module mm.addons.mod_label + * @ngdoc method + * @name $mmaModLabelHandlers#courseContent + */ + self.courseContent = function() { + + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @return {Boolean} + */ + self.isEnabled = function() { + return true; + }; + + /** + * Get the controller. + * + * @param {Object} module The module info. + * @return {Function} + */ + self.getController = function(module) { + return function($scope) { + var title = $mmText.shortenText($mmText.cleanTags(module.description).trim(), 128); + if (title.length <= 0) { + $translate('mma.mod_label.taptoview').then(function(taptoview) { + $scope.title = '' + taptoview + ''; + }); + } else { + $scope.title = title; + } + + $scope.icon = false; + $scope.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $state.go('site.mod_label', {description: module.description}); + }; + }; + }; + + return self; + }; + + /** + * Content links handler. + * + * @module mm.addons.mod_label + * @ngdoc method + * @name $mmaModLabelHandlers#linksHandler + */ + self.linksHandler = function() { + + var self = {}; + + /** + * Whether or not the handler is enabled for a certain site. + * + * @param {String} siteId Site ID. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with true if enabled. + */ + function isEnabled(siteId, courseId) { + if (courseId) { + return $q.when(true); + } + return $mmCourse.canGetModuleWithoutCourseId(siteId); + } + + /** + * Get actions to perform with the link. + * + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with the list of actions. + * See {@link $mmContentLinksDelegate#registerLinkHandler}. + */ + self.getActions = function(siteIds, url, courseId) { + // Check it's a label URL. + if (url.indexOf('/mod/label/view.php') > -1) { + return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); + } + return $q.when([]); + }; + + return self; + }; + + return self; +}); diff --git a/www/addons/mod_lti/main.js b/www/addons/mod_lti/main.js index 36b335483fa..042a1a42573 100644 --- a/www/addons/mod_lti/main.js +++ b/www/addons/mod_lti/main.js @@ -36,6 +36,7 @@ angular.module('mm.addons.mod_lti', []) }) -.config(function($mmCourseDelegateProvider) { - $mmCourseDelegateProvider.registerContentHandler('mmaModLti', 'lti', '$mmaModLtiCourseContentHandler'); +.config(function($mmCourseDelegateProvider, $mmContentLinksDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModLti', 'lti', '$mmaModLtiHandlers.courseContent'); + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModLti', '$mmaModLtiHandlers.linksHandler'); }); diff --git a/www/addons/mod_lti/services/course_content_handler.js b/www/addons/mod_lti/services/course_content_handler.js deleted file mode 100644 index 7e0aa3d2134..00000000000 --- a/www/addons/mod_lti/services/course_content_handler.js +++ /dev/null @@ -1,111 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -angular.module('mm.addons.mod_lti') - -/** - * Mod LTI course content handler. - * - * @module mm.addons.mod_lti - * @ngdoc service - * @name $mmaModLtiCourseContentHandler - */ -.factory('$mmaModLtiCourseContentHandler', function($mmCourse, $mmaModLti, $state, $mmSite, $mmFilepool, $mmApp, $mmUtil, - mmaModLtiComponent) { - var self = {}; - - /** - * Whether or not the module is enabled for the site. - * - * @module mm.addons.mod_lti - * @ngdoc method - * @name $mmaModLtiCourseContentHandler#isEnabled - * @return {Boolean} - */ - self.isEnabled = function() { - return $mmaModLti.isPluginEnabled(); - }; - - /** - * Get the controller. - * - * @module mm.addons.mod_lti - * @ngdoc method - * @name $mmaModLtiCourseContentHandler#isEnabled - * @param {Object} module The module info. - * @param {Number} courseid The course ID. - * @return {Function} - */ - self.getController = function(module, courseid) { - return function($scope) { - $scope.title = module.name; - $scope.icon = $mmCourse.getModuleIconSrc('lti'); // Get LTI default icon for now. - $scope.action = function() { - $state.go('site.mod_lti', {module: module, courseid: courseid}); - }; - - // Get LTI data. - var promise = $mmaModLti.getLti(courseid, module.id); - - // Handle custom icons. - promise.then(function(ltidata) { - var icon = ltidata.secureicon || ltidata.icon; - if (icon) { - $mmFilepool.downloadUrl($mmSite.getId(), icon, false, mmaModLtiComponent, module.id).then(function(url) { - $scope.icon = url; - }).catch(function() { - // Error downloading. If we're online we'll set the online url. - if ($mmApp.isOnline()) { - $scope.icon = icon; - } - }); - } - }); - - // Button to launch the LTI. - $scope.buttons = [{ - icon: 'ion-link', - label: 'mma.mod_lti.launchactivity', - action: function(e) { - e.preventDefault(); - e.stopPropagation(); - - var modal = $mmUtil.showModalLoading('mm.core.loading', true); - // Get LTI and launch data. - promise.then(function(ltidata) { - return $mmaModLti.getLtiLaunchData(ltidata.id).then(function(launchdata) { - // "View" LTI. - $mmaModLti.logView(ltidata.id).then(function() { - $mmCourse.checkModuleCompletion(courseid, module.completionstatus); - }); - - // Launch LTI. - return $mmaModLti.launch(launchdata.endpoint, launchdata.parameters); - }); - }).catch(function(message) { - if (message) { - $mmUtil.showErrorModal(message); - } else { - $mmUtil.showErrorModal('mma.mod_lti.errorgetlti', true); - } - }).finally(function() { - modal.dismiss(); - }); - } - }]; - }; - }; - - return self; -}); diff --git a/www/addons/mod_lti/services/handlers.js b/www/addons/mod_lti/services/handlers.js new file mode 100644 index 00000000000..7e43b5315a6 --- /dev/null +++ b/www/addons/mod_lti/services/handlers.js @@ -0,0 +1,165 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +angular.module('mm.addons.mod_lti') + +/** + * Mod LTI handler. + * + * @module mm.addons.mod_lti + * @ngdoc service + * @name $mmaModLtiHandlers + */ +.factory('$mmaModLtiHandlers', function($mmCourse, $mmaModLti, $state, $mmSite, $mmFilepool, $mmApp, $mmUtil, + mmaModLtiComponent, $mmContentLinksHelper, $q) { + var self = {}; + + /** + * Course content handler. + * + * @module mm.addons.mod_lti + * @ngdoc method + * @name $mmaModLtiHandlers#courseContent + */ + self.courseContent = function() { + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @return {Boolean} + */ + self.isEnabled = function() { + return $mmaModLti.isPluginEnabled(); + }; + + /** + * Get the controller. + * + * @param {Object} module The module info. + * @param {Number} courseid The course ID. + * @return {Function} + */ + self.getController = function(module, courseid) { + return function($scope) { + $scope.title = module.name; + $scope.icon = $mmCourse.getModuleIconSrc('lti'); // Get LTI default icon for now. + $scope.action = function() { + $state.go('site.mod_lti', {module: module, courseid: courseid}); + }; + + // Get LTI data. + var promise = $mmaModLti.getLti(courseid, module.id); + + // Handle custom icons. + promise.then(function(ltidata) { + var icon = ltidata.secureicon || ltidata.icon; + if (icon) { + $mmFilepool.downloadUrl($mmSite.getId(), icon, false, mmaModLtiComponent, module.id).then(function(url) { + $scope.icon = url; + }).catch(function() { + // Error downloading. If we're online we'll set the online url. + if ($mmApp.isOnline()) { + $scope.icon = icon; + } + }); + } + }); + + // Button to launch the LTI. + $scope.buttons = [{ + icon: 'ion-link', + label: 'mma.mod_lti.launchactivity', + action: function(e) { + e.preventDefault(); + e.stopPropagation(); + + var modal = $mmUtil.showModalLoading('mm.core.loading', true); + // Get LTI and launch data. + promise.then(function(ltidata) { + return $mmaModLti.getLtiLaunchData(ltidata.id).then(function(launchdata) { + // "View" LTI. + $mmaModLti.logView(ltidata.id).then(function() { + $mmCourse.checkModuleCompletion(courseid, module.completionstatus); + }); + + // Launch LTI. + return $mmaModLti.launch(launchdata.endpoint, launchdata.parameters); + }); + }).catch(function(message) { + if (message) { + $mmUtil.showErrorModal(message); + } else { + $mmUtil.showErrorModal('mma.mod_lti.errorgetlti', true); + } + }).finally(function() { + modal.dismiss(); + }); + } + }]; + }; + }; + + return self; + }; + + /** + * Content links handler. + * + * @module mm.addons.mod_lti + * @ngdoc method + * @name $mmaModLtiHandlers#linksHandler + */ + self.linksHandler = function() { + + var self = {}; + + /** + * Whether or not the handler is enabled for a certain site. + * + * @param {String} siteId Site ID. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with true if enabled. + */ + function isEnabled(siteId, courseId) { + return $mmaModLti.isPluginEnabled(siteId).then(function(enabled) { + if (!enabled) { + return false; + } + return courseId || $mmCourse.canGetModuleWithoutCourseId(siteId); + }); + } + + /** + * Get actions to perform with the link. + * + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with the list of actions. + * See {@link $mmContentLinksDelegate#registerLinkHandler}. + */ + self.getActions = function(siteIds, url, courseId) { + // Check it's a LTI URL. + if (url.indexOf('/mod/lti/view.php') > -1) { + return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); + } + return $q.when([]); + }; + + return self; + }; + + return self; +}); diff --git a/www/addons/mod_lti/services/lti.js b/www/addons/mod_lti/services/lti.js index e11eaa89737..b9ca8c2bcf6 100644 --- a/www/addons/mod_lti/services/lti.js +++ b/www/addons/mod_lti/services/lti.js @@ -21,7 +21,7 @@ angular.module('mm.addons.mod_lti') * @ngdoc service * @name $mmaModLti */ -.factory('$mmaModLti', function($q, $mmSite, $mmFS, $mmText, $mmUtil, $mmLang) { +.factory('$mmaModLti', function($q, $mmSite, $mmFS, $mmText, $mmUtil, $mmLang, $mmSitesManager) { var self = {}, launcherFileName = 'lti_launcher.html'; @@ -182,16 +182,21 @@ angular.module('mm.addons.mod_lti') }; /** - * Return whether or not the plugin is enabled. Plugin is enabled if the lti WS are available. + * Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the lti WS are available. * * @module mm.addons.mod_lti * @ngdoc method * @name $mmaModLti#isPluginEnabled - * @return {Boolean} True if plugin is enabled, false otherwise. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. */ - self.isPluginEnabled = function() { - return $mmSite.wsAvailable('mod_lti_get_ltis_by_courses') && - $mmSite.wsAvailable('mod_lti_get_tool_launch_data'); + self.isPluginEnabled = function(siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.wsAvailable('mod_lti_get_ltis_by_courses') && + site.wsAvailable('mod_lti_get_tool_launch_data'); + }); }; /** diff --git a/www/addons/mod_page/main.js b/www/addons/mod_page/main.js index 2b09957238e..8cb75bc0aba 100644 --- a/www/addons/mod_page/main.js +++ b/www/addons/mod_page/main.js @@ -36,7 +36,8 @@ angular.module('mm.addons.mod_page', ['mm.core']) }) -.config(function($mmCourseDelegateProvider, $mmCoursePrefetchDelegateProvider) { - $mmCourseDelegateProvider.registerContentHandler('mmaModPage', 'page', '$mmaModPageCourseContentHandler'); +.config(function($mmCourseDelegateProvider, $mmCoursePrefetchDelegateProvider, $mmContentLinksDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModPage', 'page', '$mmaModPageHandlers.courseContent'); $mmCoursePrefetchDelegateProvider.registerPrefetchHandler('mmaModPage', 'page', '$mmaModPagePrefetchHandler'); + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModPage', '$mmaModPageHandlers.linksHandler'); }); diff --git a/www/addons/mod_page/services/course_content_handler.js b/www/addons/mod_page/services/course_content_handler.js deleted file mode 100644 index 882e22699f5..00000000000 --- a/www/addons/mod_page/services/course_content_handler.js +++ /dev/null @@ -1,128 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -angular.module('mm.addons.mod_page') - -/** - * Mod page course content handler. - * - * @module mm.addons.mod_page - * @ngdoc service - * @name $mmaModPageCourseContentHandler - */ -.factory('$mmaModPageCourseContentHandler', function($mmCourse, $mmaModPage, $mmEvents, $state, $mmSite, $mmUtil, $mmFilepool, - $mmCoursePrefetchDelegate, mmCoreDownloading, mmCoreNotDownloaded, mmCoreOutdated, mmCoreEventPackageStatusChanged, - mmaModPageComponent) { - - var self = {}; - - /** - * Whether or not the module is enabled for the site. - * - * @module mm.addons.mod_page - * @ngdoc method - * @name $mmaModPageCourseContentHandler#isEnabled - * @return {Boolean} - */ - self.isEnabled = function() { - return $mmSite.canDownloadFiles(); - }; - - /** - * Get the controller. - * - * @module mm.addons.mod_page - * @ngdoc method - * @name $mmaModPageCourseContentHandler#isEnabled - * @param {Object} module The module info. - * @param {Number} courseid The course ID. - * @return {Function} - */ - self.getController = function(module, courseid) { - return function($scope) { - var downloadBtn, - refreshBtn, - revision = $mmFilepool.getRevisionFromFileList(module.contents), - timemodified = $mmFilepool.getTimemodifiedFromFileList(module.contents); - - downloadBtn = { - hidden: true, - icon: 'ion-ios-cloud-download-outline', - label: 'mm.core.download', - action: function(e) { - e.preventDefault(); - e.stopPropagation(); - $mmaModPage.prefetchContent(module).catch(function() { - if (!$scope.$$destroyed) { - $mmUtil.showErrorModal('mm.core.errordownloading', true); - } - }); - } - }; - - refreshBtn = { - icon: 'ion-android-refresh', - label: 'mm.core.refresh', - hidden: true, - action: function(e) { - e.preventDefault(); - e.stopPropagation(); - $mmaModPage.invalidateContent(module.id).finally(function() { - $mmaModPage.prefetchContent(module).catch(function() { - if (!$scope.$$destroyed) { - $mmUtil.showErrorModal('mm.core.errordownloading', true); - } - }); - }); - } - }; - - $scope.title = module.name; - $scope.icon = $mmCourse.getModuleIconSrc('page'); - $scope.buttons = [downloadBtn, refreshBtn]; - $scope.spinner = false; - - $scope.action = function(e) { - e.preventDefault(); - e.stopPropagation(); - $state.go('site.mod_page', {module: module, courseid: courseid}); - }; - - // Show buttons according to module status. - function showStatus(status) { - if (status) { - $scope.spinner = status === mmCoreDownloading; - downloadBtn.hidden = status !== mmCoreNotDownloaded; - refreshBtn.hidden = status !== mmCoreOutdated; - } - } - - // Listen for changes on this module status. - var statusObserver = $mmEvents.on(mmCoreEventPackageStatusChanged, function(data) { - if (data.siteid === $mmSite.getId() && data.componentId === module.id && data.component === mmaModPageComponent) { - showStatus(data.status); - } - }); - - // Get current status to decide which icon should be shown. - $mmCoursePrefetchDelegate.getModuleStatus(module, courseid, revision, timemodified).then(showStatus); - - $scope.$on('$destroy', function() { - statusObserver && statusObserver.off && statusObserver.off(); - }); - }; - }; - - return self; -}); diff --git a/www/addons/mod_page/services/handlers.js b/www/addons/mod_page/services/handlers.js new file mode 100644 index 00000000000..4d9244b09fd --- /dev/null +++ b/www/addons/mod_page/services/handlers.js @@ -0,0 +1,184 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +angular.module('mm.addons.mod_page') + +/** + * Mod page handlers. + * + * @module mm.addons.mod_page + * @ngdoc service + * @name $mmaModPageHandlers + */ +.factory('$mmaModPageHandlers', function($mmCourse, $mmaModPage, $mmEvents, $state, $mmSite, $mmUtil, $mmFilepool, + $mmCoursePrefetchDelegate, mmCoreDownloading, mmCoreNotDownloaded, mmCoreOutdated, mmCoreEventPackageStatusChanged, + mmaModPageComponent, $mmContentLinksHelper, $q) { + var self = {}; + + /** + * Course content handler. + * + * @module mm.addons.mod_page + * @ngdoc method + * @name $mmaModPageHandlers#courseContent + */ + self.courseContent = function() { + + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @return {Boolean} + */ + self.isEnabled = function() { + return $mmaModPage.isPluginEnabled(); + }; + + /** + * Get the controller. + * + * @param {Object} module The module info. + * @param {Number} courseid The course ID. + * @return {Function} + */ + self.getController = function(module, courseid) { + return function($scope) { + var downloadBtn, + refreshBtn, + revision = $mmFilepool.getRevisionFromFileList(module.contents), + timemodified = $mmFilepool.getTimemodifiedFromFileList(module.contents); + + downloadBtn = { + hidden: true, + icon: 'ion-ios-cloud-download-outline', + label: 'mm.core.download', + action: function(e) { + e.preventDefault(); + e.stopPropagation(); + $mmaModPage.prefetchContent(module).catch(function() { + if (!$scope.$$destroyed) { + $mmUtil.showErrorModal('mm.core.errordownloading', true); + } + }); + } + }; + + refreshBtn = { + icon: 'ion-android-refresh', + label: 'mm.core.refresh', + hidden: true, + action: function(e) { + e.preventDefault(); + e.stopPropagation(); + $mmaModPage.invalidateContent(module.id).finally(function() { + $mmaModPage.prefetchContent(module).catch(function() { + if (!$scope.$$destroyed) { + $mmUtil.showErrorModal('mm.core.errordownloading', true); + } + }); + }); + } + }; + + $scope.title = module.name; + $scope.icon = $mmCourse.getModuleIconSrc('page'); + $scope.buttons = [downloadBtn, refreshBtn]; + $scope.spinner = false; + + $scope.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $state.go('site.mod_page', {module: module, courseid: courseid}); + }; + + // Show buttons according to module status. + function showStatus(status) { + if (status) { + $scope.spinner = status === mmCoreDownloading; + downloadBtn.hidden = status !== mmCoreNotDownloaded; + refreshBtn.hidden = status !== mmCoreOutdated; + } + } + + // Listen for changes on this module status. + var statusObserver = $mmEvents.on(mmCoreEventPackageStatusChanged, function(data) { + if (data.siteid === $mmSite.getId() && data.componentId === module.id && data.component === mmaModPageComponent) { + showStatus(data.status); + } + }); + + // Get current status to decide which icon should be shown. + $mmCoursePrefetchDelegate.getModuleStatus(module, courseid, revision, timemodified).then(showStatus); + + $scope.$on('$destroy', function() { + statusObserver && statusObserver.off && statusObserver.off(); + }); + }; + }; + + return self; + }; + + /** + * Content links handler. + * + * @module mm.addons.mod_page + * @ngdoc method + * @name $mmaModPageHandlers#linksHandler + */ + self.linksHandler = function() { + + var self = {}; + + /** + * Whether or not the handler is enabled for a certain site. + * + * @param {String} siteId Site ID. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with true if enabled. + */ + function isEnabled(siteId, courseId) { + return $mmaModPage.isPluginEnabled(siteId).then(function(enabled) { + if (!enabled) { + return false; + } + return courseId || $mmCourse.canGetModuleWithoutCourseId(siteId); + }); + } + + /** + * Get actions to perform with the link. + * + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with the list of actions. + * See {@link $mmContentLinksDelegate#registerLinkHandler}. + */ + self.getActions = function(siteIds, url, courseId) { + // Check it's a page URL. + if (url.indexOf('/mod/page/view.php') > -1) { + return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); + } + return $q.when([]); + }; + + return self; + }; + + return self; +}); diff --git a/www/addons/mod_page/services/page.js b/www/addons/mod_page/services/page.js index c8a7468bf81..6a0055f43d0 100644 --- a/www/addons/mod_page/services/page.js +++ b/www/addons/mod_page/services/page.js @@ -21,7 +21,7 @@ angular.module('mm.addons.mod_page') * @ngdoc service * @name $mmaModPage */ -.factory('$mmaModPage', function($mmFilepool, $mmSite, $mmFS, $http, $log, $q, mmaModPageComponent) { +.factory('$mmaModPage', function($mmFilepool, $mmSite, $mmFS, $http, $log, $q, $mmSitesManager, mmaModPageComponent) { $log = $log.getInstance('$mmaModPage'); var self = {}; @@ -244,6 +244,23 @@ angular.module('mm.addons.mod_page') return (filename === 'index.html' && (fileurl.indexOf(url) > 0 || fileurl.indexOf(encodedUrl) > 0 )); }; + /** + * Check if page plugin is enabled in a certain site. + * + * @module mm.addons.mod_page + * @ngdoc method + * @name $mmaModPage#isPluginEnabled + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. + */ + self.isPluginEnabled = function(siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.canDownloadFiles(); + }); + }; + /** * Report a page as being viewed. * diff --git a/www/addons/mod_resource/main.js b/www/addons/mod_resource/main.js index 705398d3d2f..194d24ae212 100644 --- a/www/addons/mod_resource/main.js +++ b/www/addons/mod_resource/main.js @@ -36,7 +36,8 @@ angular.module('mm.addons.mod_resource', ['mm.core']) }) -.config(function($mmCourseDelegateProvider, $mmCoursePrefetchDelegateProvider) { - $mmCourseDelegateProvider.registerContentHandler('mmaModResource', 'resource', '$mmaModResourceCourseContentHandler'); +.config(function($mmCourseDelegateProvider, $mmCoursePrefetchDelegateProvider, $mmContentLinksDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModResource', 'resource', '$mmaModResourceHandlers.courseContent'); $mmCoursePrefetchDelegateProvider.registerPrefetchHandler('mmaModResource', 'resource', '$mmaModResourcePrefetchHandler'); + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModResource', '$mmaModResourceHandlers.linksHandler'); }); diff --git a/www/addons/mod_resource/services/course_content_handler.js b/www/addons/mod_resource/services/course_content_handler.js deleted file mode 100644 index cc69787c32f..00000000000 --- a/www/addons/mod_resource/services/course_content_handler.js +++ /dev/null @@ -1,140 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -angular.module('mm.addons.mod_resource') - -/** - * Mod resource course content handler. - * - * @module mm.addons.mod_resource - * @ngdoc service - * @name $mmaModResourceCourseContentHandler - */ -.factory('$mmaModResourceCourseContentHandler', function($mmCourse, $mmaModResource, $mmEvents, $state, $mmSite, $mmUtil, - $mmCoursePrefetchDelegate, $mmFilepool, $mmFS, mmCoreDownloading, mmCoreNotDownloaded, mmCoreOutdated, - mmCoreEventPackageStatusChanged, mmaModResourceComponent) { - - var self = {}; - - /** - * Whether or not the module is enabled for the site. - * - * @module mm.addons.mod_resource - * @ngdoc method - * @name $mmaModResourceCourseContentHandler#isEnabled - * @return {Boolean} - */ - self.isEnabled = function() { - return $mmSite.canDownloadFiles(); - }; - - /** - * Get the controller. - * - * @module mm.addons.mod_resource - * @ngdoc method - * @name $mmaModResourceCourseContentHandler#getController - * @param {Object} module The module info. - * @param {Number} courseid The course ID. - * @return {Function} - */ - self.getController = function(module, courseid) { - return function($scope) { - var downloadBtn, - refreshBtn, - revision = $mmFilepool.getRevisionFromFileList(module.contents), - timemodified = $mmFilepool.getTimemodifiedFromFileList(module.contents); - - downloadBtn = { - hidden: true, - icon: 'ion-ios-cloud-download-outline', - label: 'mm.core.download', - action: function(e) { - e.preventDefault(); - e.stopPropagation(); - $mmaModResource.prefetchContent(module).catch(function() { - if (!$scope.$$destroyed) { - $mmUtil.showErrorModal('mm.core.errordownloading', true); - } - }); - } - }; - - refreshBtn = { - icon: 'ion-android-refresh', - label: 'mm.core.refresh', - hidden: true, - action: function(e) { - e.preventDefault(); - e.stopPropagation(); - $mmaModResource.invalidateContent(module.id).finally(function() { - $mmaModResource.prefetchContent(module).catch(function() { - if (!$scope.$$destroyed) { - $mmUtil.showErrorModal('mm.core.errordownloading', true); - } - }); - }); - } - }; - - $scope.title = module.name; - - if (module.contents.length) { - var filename = module.contents[0].filename, - extension = $mmFS.getFileExtension(filename); - if (module.contents.length == 1 || (extension != "html" && extension != "htm")) { - $scope.icon = $mmFS.getFileIcon(filename); - } else { - $scope.icon = $mmCourse.getModuleIconSrc('resource'); - } - } else { - $scope.icon = $mmCourse.getModuleIconSrc('resource'); - } - $scope.buttons = [downloadBtn, refreshBtn]; - $scope.spinner = false; - - $scope.action = function(e) { - e.preventDefault(); - e.stopPropagation(); - $state.go('site.mod_resource', {module: module, courseid: courseid}); - }; - - // Show buttons according to module status. - function showStatus(status) { - if (status) { - $scope.spinner = status === mmCoreDownloading; - downloadBtn.hidden = status !== mmCoreNotDownloaded; - refreshBtn.hidden = status !== mmCoreOutdated; - } - } - - // Listen for changes on this module status. - var statusObserver = $mmEvents.on(mmCoreEventPackageStatusChanged, function(data) { - if (data.siteid === $mmSite.getId() && data.componentId === module.id && - data.component === mmaModResourceComponent) { - showStatus(data.status); - } - }); - - // Get current status to decide which icon should be shown. - $mmCoursePrefetchDelegate.getModuleStatus(module, courseid, revision, timemodified).then(showStatus); - - $scope.$on('$destroy', function() { - statusObserver && statusObserver.off && statusObserver.off(); - }); - }; - }; - - return self; -}); diff --git a/www/addons/mod_resource/services/handlers.js b/www/addons/mod_resource/services/handlers.js new file mode 100644 index 00000000000..05a50263871 --- /dev/null +++ b/www/addons/mod_resource/services/handlers.js @@ -0,0 +1,196 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +angular.module('mm.addons.mod_resource') + +/** + * Mod resource handlers. + * + * @module mm.addons.mod_resource + * @ngdoc service + * @name $mmaModResourceHandlers + */ +.factory('$mmaModResourceHandlers', function($mmCourse, $mmaModResource, $mmEvents, $state, $mmSite, $mmUtil, + $mmCoursePrefetchDelegate, $mmFilepool, $mmFS, mmCoreDownloading, mmCoreNotDownloaded, mmCoreOutdated, + mmCoreEventPackageStatusChanged, mmaModResourceComponent, $q, $mmContentLinksHelper) { + var self = {}; + + /** + * Course content handler. + * + * @module mm.addons.mod_resource + * @ngdoc method + * @name $mmaModResourceHandlers#courseContent + */ + self.courseContent = function() { + + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @return {Boolean} + */ + self.isEnabled = function() { + return $mmaModResource.isPluginEnabled(); + }; + + /** + * Get the controller. + * + * @param {Object} module The module info. + * @param {Number} courseid The course ID. + * @return {Function} + */ + self.getController = function(module, courseid) { + return function($scope) { + var downloadBtn, + refreshBtn, + revision = $mmFilepool.getRevisionFromFileList(module.contents), + timemodified = $mmFilepool.getTimemodifiedFromFileList(module.contents); + + downloadBtn = { + hidden: true, + icon: 'ion-ios-cloud-download-outline', + label: 'mm.core.download', + action: function(e) { + e.preventDefault(); + e.stopPropagation(); + $mmaModResource.prefetchContent(module).catch(function() { + if (!$scope.$$destroyed) { + $mmUtil.showErrorModal('mm.core.errordownloading', true); + } + }); + } + }; + + refreshBtn = { + icon: 'ion-android-refresh', + label: 'mm.core.refresh', + hidden: true, + action: function(e) { + e.preventDefault(); + e.stopPropagation(); + $mmaModResource.invalidateContent(module.id).finally(function() { + $mmaModResource.prefetchContent(module).catch(function() { + if (!$scope.$$destroyed) { + $mmUtil.showErrorModal('mm.core.errordownloading', true); + } + }); + }); + } + }; + + $scope.title = module.name; + + if (module.contents.length) { + var filename = module.contents[0].filename, + extension = $mmFS.getFileExtension(filename); + if (module.contents.length == 1 || (extension != "html" && extension != "htm")) { + $scope.icon = $mmFS.getFileIcon(filename); + } else { + $scope.icon = $mmCourse.getModuleIconSrc('resource'); + } + } else { + $scope.icon = $mmCourse.getModuleIconSrc('resource'); + } + $scope.buttons = [downloadBtn, refreshBtn]; + $scope.spinner = false; + + $scope.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $state.go('site.mod_resource', {module: module, courseid: courseid}); + }; + + // Show buttons according to module status. + function showStatus(status) { + if (status) { + $scope.spinner = status === mmCoreDownloading; + downloadBtn.hidden = status !== mmCoreNotDownloaded; + refreshBtn.hidden = status !== mmCoreOutdated; + } + } + + // Listen for changes on this module status. + var statusObserver = $mmEvents.on(mmCoreEventPackageStatusChanged, function(data) { + if (data.siteid === $mmSite.getId() && data.componentId === module.id && + data.component === mmaModResourceComponent) { + showStatus(data.status); + } + }); + + // Get current status to decide which icon should be shown. + $mmCoursePrefetchDelegate.getModuleStatus(module, courseid, revision, timemodified).then(showStatus); + + $scope.$on('$destroy', function() { + statusObserver && statusObserver.off && statusObserver.off(); + }); + }; + }; + + return self; + }; + + /** + * Content links handler. + * + * @module mm.addons.mod_resource + * @ngdoc method + * @name $mmaModResourceHandlers#linksHandler + */ + self.linksHandler = function() { + + var self = {}; + + /** + * Whether or not the handler is enabled for a certain site. + * + * @param {String} siteId Site ID. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with true if enabled. + */ + function isEnabled(siteId, courseId) { + return $mmaModResource.isPluginEnabled(siteId).then(function(enabled) { + if (!enabled) { + return false; + } + return courseId || $mmCourse.canGetModuleWithoutCourseId(siteId); + }); + } + + /** + * Get actions to perform with the link. + * + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with the list of actions. + * See {@link $mmContentLinksDelegate#registerLinkHandler}. + */ + self.getActions = function(siteIds, url, courseId) { + // Check it's a resource URL. + if (url.indexOf('/mod/resource/view.php') > -1) { + return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); + } + return $q.when([]); + }; + + return self; + }; + + return self; +}); diff --git a/www/addons/mod_resource/services/resource.js b/www/addons/mod_resource/services/resource.js index 63078156796..fdd4bab796f 100644 --- a/www/addons/mod_resource/services/resource.js +++ b/www/addons/mod_resource/services/resource.js @@ -21,7 +21,7 @@ angular.module('mm.addons.mod_resource') * @ngdoc service * @name $mmaModResource */ -.factory('$mmaModResource', function($mmFilepool, $mmSite, $mmUtil, $mmFS, $http, $log, $q, $sce, $mmApp, +.factory('$mmaModResource', function($mmFilepool, $mmSite, $mmUtil, $mmFS, $http, $log, $q, $sce, $mmApp, $mmSitesManager, mmaModResourceComponent) { $log = $log.getInstance('$mmaModResource'); @@ -323,6 +323,23 @@ angular.module('mm.addons.mod_resource') return file.type === 'file'; }; + /** + * Check if resource plugin is enabled in a certain site. + * + * @module mm.addons.mod_resource + * @ngdoc method + * @name $mmaModResource#isPluginEnabled + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. + */ + self.isPluginEnabled = function(siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.canDownloadFiles(); + }); + }; + /** * Report the resource as being viewed. * diff --git a/www/addons/mod_scorm/main.js b/www/addons/mod_scorm/main.js index bcc82fa313b..a893aab2c46 100644 --- a/www/addons/mod_scorm/main.js +++ b/www/addons/mod_scorm/main.js @@ -59,9 +59,10 @@ angular.module('mm.addons.mod_scorm', ['mm.core']) }) -.config(function($mmCourseDelegateProvider, $mmCoursePrefetchDelegateProvider) { - $mmCourseDelegateProvider.registerContentHandler('mmaModScorm', 'scorm', '$mmaModScormCourseContentHandler'); +.config(function($mmCourseDelegateProvider, $mmCoursePrefetchDelegateProvider, $mmContentLinksDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModScorm', 'scorm', '$mmaModScormHandlers.courseContent'); $mmCoursePrefetchDelegateProvider.registerPrefetchHandler('mmaModScorm', 'scorm', '$mmaModScormPrefetchHandler'); + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModScorm', '$mmaModScormHandlers.linksHandler'); }) .run(function($timeout, $mmaModScormSync, $mmApp, $mmEvents, $mmSite, mmCoreEventLogin) { diff --git a/www/addons/mod_scorm/services/course_content_handler.js b/www/addons/mod_scorm/services/course_content_handler.js deleted file mode 100644 index cb837c73d0a..00000000000 --- a/www/addons/mod_scorm/services/course_content_handler.js +++ /dev/null @@ -1,138 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -angular.module('mm.addons.mod_scorm') - -/** - * Mod scorm course content handler. - * - * @module mm.addons.mod_scorm - * @ngdoc service - * @name $mmaModScormCourseContentHandler - */ -.factory('$mmaModScormCourseContentHandler', function($mmCourse, $mmaModScorm, $mmEvents, $state, $mmSite, $mmaModScormHelper, - $mmCoursePrefetchDelegate, mmCoreDownloading, mmCoreNotDownloaded, mmCoreOutdated, mmCoreEventPackageStatusChanged, - mmaModScormComponent) { - var self = {}; - - /** - * Whether or not the module is enabled for the site. - * - * @module mm.addons.mod_scorm - * @ngdoc method - * @name $mmaModScormCourseContentHandler#isEnabled - * @return {Boolean} - */ - self.isEnabled = function() { - return $mmaModScorm.isPluginEnabled(); - }; - - /** - * Get the controller. - * - * @module mm.addons.mod_scorm - * @ngdoc method - * @name $mmaModScormCourseContentHandler#getController - * @param {Object} module The module info. - * @param {Number} courseid The course ID. - * @return {Function} - */ - self.getController = function(module, courseid) { - return function($scope) { - var downloadBtn, - refreshBtn; - - // Create the buttons without action yet. This is to prevent a glitch in the view. - downloadBtn = { - hidden: true, - icon: 'ion-ios-cloud-download', - label: 'mm.core.download' - }; - - refreshBtn = { - icon: 'ion-android-refresh', - label: 'mm.core.refresh', - hidden: true - }; - - $scope.icon = $mmCourse.getModuleIconSrc('scorm'); - $scope.title = module.name; - $scope.buttons = [downloadBtn, refreshBtn]; - $scope.spinner = false; - - $scope.action = function(e) { - e.preventDefault(); - e.stopPropagation(); - $state.go('site.mod_scorm', {module: module, courseid: courseid}); - }; - - // Retrieve SCORM to calculate the rest of data. - $mmaModScorm.getScorm(courseid, module.id, module.url).then(function(scorm) { - var revision = scorm.sha1hash, - timemodified = 0; - - function download() { - $mmaModScormHelper.confirmDownload(scorm).then(function() { - $mmaModScorm.prefetch(scorm).catch(function() { - if (!$scope.$$destroyed) { - $mmaModScormHelper.showDownloadError(scorm); - } - }); - }); - } - - // Now add the action to the buttons. - downloadBtn.action = function(e) { - e.preventDefault(); - e.stopPropagation(); - download(); - }; - - refreshBtn.action = function(e) { - e.preventDefault(); - e.stopPropagation(); - $mmaModScorm.invalidateContent(scorm.coursemodule).finally(function() { - download(); - }); - }; - - // Show buttons according to module status. - function showStatus(status) { - if (status) { - $scope.spinner = status === mmCoreDownloading; - downloadBtn.hidden = status !== mmCoreNotDownloaded; - refreshBtn.hidden = status !== mmCoreOutdated; - } - } - - // Listen for changes on this module status. - var statusObserver = $mmEvents.on(mmCoreEventPackageStatusChanged, function(data) { - if (data.siteid === $mmSite.getId() && data.componentId === scorm.coursemodule && - data.component === mmaModScormComponent) { - showStatus(data.status); - } - }); - - // Get current status to decide which icon should be shown. - $mmCoursePrefetchDelegate.getModuleStatus(module, courseid, revision, timemodified).then(showStatus); - - $scope.$on('$destroy', function() { - statusObserver && statusObserver.off && statusObserver.off(); - }); - }); - }; - }; - - return self; -}); diff --git a/www/addons/mod_scorm/services/handlers.js b/www/addons/mod_scorm/services/handlers.js new file mode 100644 index 00000000000..ebeee392b9f --- /dev/null +++ b/www/addons/mod_scorm/services/handlers.js @@ -0,0 +1,199 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +angular.module('mm.addons.mod_scorm') + +/** + * Mod scorm handlers. + * + * @module mm.addons.mod_scorm + * @ngdoc service + * @name $mmaModScormHandlers + */ +.factory('$mmaModScormHandlers', function($mmCourse, $mmaModScorm, $mmEvents, $state, $mmSite, $mmaModScormHelper, + $mmCoursePrefetchDelegate, mmCoreDownloading, mmCoreNotDownloaded, mmCoreOutdated, mmCoreEventPackageStatusChanged, + mmaModScormComponent, $q, $mmContentLinksHelper) { + var self = {}; + + /** + * Course content handler. + * + * @module mm.addons.mod_scorm + * @ngdoc method + * @name $mmaModScormHandlers#courseContent + */ + self.courseContent = function() { + + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @return {Boolean} + */ + self.isEnabled = function() { + return $mmaModScorm.isPluginEnabled(); + }; + + /** + * Get the controller. + * + * @param {Object} module The module info. + * @param {Number} courseid The course ID. + * @return {Function} + */ + self.getController = function(module, courseid) { + return function($scope) { + var downloadBtn, + refreshBtn; + + // Create the buttons without action yet. This is to prevent a glitch in the view. + downloadBtn = { + hidden: true, + icon: 'ion-ios-cloud-download', + label: 'mm.core.download' + }; + + refreshBtn = { + icon: 'ion-android-refresh', + label: 'mm.core.refresh', + hidden: true + }; + + $scope.icon = $mmCourse.getModuleIconSrc('scorm'); + $scope.title = module.name; + $scope.buttons = [downloadBtn, refreshBtn]; + $scope.spinner = false; + + $scope.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $state.go('site.mod_scorm', {module: module, courseid: courseid}); + }; + + // Retrieve SCORM to calculate the rest of data. + $mmaModScorm.getScorm(courseid, module.id, module.url).then(function(scorm) { + var revision = scorm.sha1hash, + timemodified = 0; + + function download() { + $mmaModScormHelper.confirmDownload(scorm).then(function() { + $mmaModScorm.prefetch(scorm).catch(function() { + if (!$scope.$$destroyed) { + $mmaModScormHelper.showDownloadError(scorm); + } + }); + }); + } + + // Now add the action to the buttons. + downloadBtn.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + download(); + }; + + refreshBtn.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $mmaModScorm.invalidateContent(scorm.coursemodule).finally(function() { + download(); + }); + }; + + // Show buttons according to module status. + function showStatus(status) { + if (status) { + $scope.spinner = status === mmCoreDownloading; + downloadBtn.hidden = status !== mmCoreNotDownloaded; + refreshBtn.hidden = status !== mmCoreOutdated; + } + } + + // Listen for changes on this module status. + var statusObserver = $mmEvents.on(mmCoreEventPackageStatusChanged, function(data) { + if (data.siteid === $mmSite.getId() && data.componentId === scorm.coursemodule && + data.component === mmaModScormComponent) { + showStatus(data.status); + } + }); + + // Get current status to decide which icon should be shown. + $mmCoursePrefetchDelegate.getModuleStatus(module, courseid, revision, timemodified).then(showStatus); + + $scope.$on('$destroy', function() { + statusObserver && statusObserver.off && statusObserver.off(); + }); + }); + }; + }; + + return self; + }; + + /** + * Content links handler. + * + * @module mm.addons.mod_scorm + * @ngdoc method + * @name $mmaModScormHandlers#linksHandler + */ + self.linksHandler = function() { + + var self = {}; + + /** + * Whether or not the handler is enabled for a certain site. + * + * @param {String} siteId Site ID. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with true if enabled. + */ + function isEnabled(siteId, courseId) { + return $mmaModScorm.isPluginEnabled(siteId).then(function(enabled) { + if (!enabled) { + return false; + } + return courseId || $mmCourse.canGetModuleWithoutCourseId(siteId); + }); + } + + /** + * Get actions to perform with the link. + * + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with the list of actions. + * See {@link $mmContentLinksDelegate#registerLinkHandler}. + */ + self.getActions = function(siteIds, url, courseId) { + // Check it's a SCORM URL. + if (url.indexOf('/mod/scorm/view.php') > -1) { + return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); + } + return $q.when([]); + }; + + return self; + }; + + return self; +}); diff --git a/www/addons/mod_scorm/services/scorm.js b/www/addons/mod_scorm/services/scorm.js index 82840e9185b..943b445407e 100644 --- a/www/addons/mod_scorm/services/scorm.js +++ b/www/addons/mod_scorm/services/scorm.js @@ -1151,20 +1151,25 @@ angular.module('mm.addons.mod_scorm') }; /** - * Return whether or not the plugin is enabled. Plugin is enabled if the scorm WS are available. + * Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the scorm WS are available. * * @module mm.addons.mod_scorm * @ngdoc method * @name $mmaModScorm#isPluginEnabled - * @return {Boolean} True if plugin is enabled, false otherwise. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. */ - self.isPluginEnabled = function() { - return $mmSite.wsAvailable('mod_scorm_get_scorm_attempt_count') && - $mmSite.wsAvailable('mod_scorm_get_scorm_sco_tracks') && - $mmSite.wsAvailable('mod_scorm_get_scorm_scoes') && - $mmSite.wsAvailable('mod_scorm_get_scorm_user_data') && - $mmSite.wsAvailable('mod_scorm_get_scorms_by_courses') && - $mmSite.wsAvailable('mod_scorm_insert_scorm_tracks'); + self.isPluginEnabled = function(siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.wsAvailable('mod_scorm_get_scorm_attempt_count') && + site.wsAvailable('mod_scorm_get_scorm_sco_tracks') && + site.wsAvailable('mod_scorm_get_scorm_scoes') && + site.wsAvailable('mod_scorm_get_scorm_user_data') && + site.wsAvailable('mod_scorm_get_scorms_by_courses') && + site.wsAvailable('mod_scorm_insert_scorm_tracks'); + }); }; /** diff --git a/www/addons/mod_survey/main.js b/www/addons/mod_survey/main.js index 2902297fa6f..5c6a9670ec2 100644 --- a/www/addons/mod_survey/main.js +++ b/www/addons/mod_survey/main.js @@ -34,6 +34,7 @@ angular.module('mm.addons.mod_survey', []) }) -.config(function($mmCourseDelegateProvider) { - $mmCourseDelegateProvider.registerContentHandler('mmaModSurvey', 'survey', '$mmaModSurveyCourseContentHandler'); +.config(function($mmCourseDelegateProvider, $mmContentLinksDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModSurvey', 'survey', '$mmaModSurveyHandlers.courseContent'); + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModSurvey', '$mmaModSurveyHandlers.linksHandler'); }); diff --git a/www/addons/mod_survey/services/course_content_handler.js b/www/addons/mod_survey/services/course_content_handler.js deleted file mode 100644 index 6dec710cfc1..00000000000 --- a/www/addons/mod_survey/services/course_content_handler.js +++ /dev/null @@ -1,60 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -angular.module('mm.addons.mod_survey') - -/** - * Mod survey course content handler. - * - * @module mm.addons.mod_survey - * @ngdoc service - * @name $mmaModSurveyCourseContentHandler - */ -.factory('$mmaModSurveyCourseContentHandler', function($mmCourse, $mmaModSurvey, $state) { - var self = {}; - - /** - * Whether or not the module is enabled for the site. - * - * @module mm.addons.mod_survey - * @ngdoc method - * @name $mmaModSurveyCourseContentHandler#isEnabled - * @return {Boolean} - */ - self.isEnabled = function() { - return $mmaModSurvey.isPluginEnabled(); - }; - - /** - * Get the controller. - * - * @module mm.addons.mod_survey - * @ngdoc method - * @name $mmaModSurveyCourseContentHandler#getController - * @param {Object} module The module info. - * @param {Number} courseid The course ID. - * @return {Function} - */ - self.getController = function(module, courseid) { - return function($scope) { - $scope.title = module.name; - $scope.icon = $mmCourse.getModuleIconSrc('survey'); - $scope.action = function() { - $state.go('site.mod_survey', {module: module, courseid: courseid}); - }; - }; - }; - - return self; -}); diff --git a/www/addons/mod_survey/services/handlers.js b/www/addons/mod_survey/services/handlers.js new file mode 100644 index 00000000000..0d98c8656a4 --- /dev/null +++ b/www/addons/mod_survey/services/handlers.js @@ -0,0 +1,119 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +angular.module('mm.addons.mod_survey') + +/** + * Mod survey handlers. + * + * @module mm.addons.mod_survey + * @ngdoc service + * @name $mmaModSurveyHandlers + */ +.factory('$mmaModSurveyHandlers', function($mmCourse, $mmaModSurvey, $state, $q, $mmContentLinksHelper) { + var self = {}; + + /** + * Course content handler. + * + * @module mm.addons.mod_survey + * @ngdoc method + * @name $mmaModSurveyHandlers#courseContent + */ + self.courseContent = function() { + + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @return {Boolean} + */ + self.isEnabled = function() { + return $mmaModSurvey.isPluginEnabled(); + }; + + /** + * Get the controller. + * + * @param {Object} module The module info. + * @param {Number} courseid The course ID. + * @return {Function} + */ + self.getController = function(module, courseid) { + return function($scope) { + $scope.title = module.name; + $scope.icon = $mmCourse.getModuleIconSrc('survey'); + $scope.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $state.go('site.mod_survey', {module: module, courseid: courseid}); + }; + }; + }; + + return self; + }; + + /** + * Content links handler. + * + * @module mm.addons.mod_survey + * @ngdoc method + * @name $mmaModSurveyHandlers#linksHandler + */ + self.linksHandler = function() { + + var self = {}; + + /** + * Whether or not the handler is enabled for a certain site. + * + * @param {String} siteId Site ID. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with true if enabled. + */ + function isEnabled(siteId, courseId) { + return $mmaModSurvey.isPluginEnabled(siteId).then(function(enabled) { + if (!enabled) { + return false; + } + return courseId || $mmCourse.canGetModuleWithoutCourseId(siteId); + }); + } + + /** + * Get actions to perform with the link. + * + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with the list of actions. + * See {@link $mmContentLinksDelegate#registerLinkHandler}. + */ + self.getActions = function(siteIds, url, courseId) { + // Check it's a survey URL. + if (url.indexOf('/mod/survey/view.php') > -1) { + return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); + } + return $q.when([]); + }; + + return self; + }; + + return self; +}); diff --git a/www/addons/mod_survey/services/survey.js b/www/addons/mod_survey/services/survey.js index 74196d03c1f..97755398044 100644 --- a/www/addons/mod_survey/services/survey.js +++ b/www/addons/mod_survey/services/survey.js @@ -21,7 +21,7 @@ angular.module('mm.addons.mod_survey') * @ngdoc service * @name $mmaModSurvey */ -.factory('$mmaModSurvey', function($q, $mmSite, $translate) { +.factory('$mmaModSurvey', function($q, $mmSite, $translate, $mmSitesManager) { var self = {}; /** @@ -243,17 +243,22 @@ angular.module('mm.addons.mod_survey') }; /** - * Return whether or not the plugin is enabled. Plugin is enabled if the survey WS are available. + * Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the survey WS are available. * * @module mm.addons.mod_survey * @ngdoc method * @name $mmaModSurvey#isPluginEnabled - * @return {Boolean} True if plugin is enabled, false otherwise. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. */ - self.isPluginEnabled = function() { - return $mmSite.wsAvailable('mod_survey_get_questions') && - $mmSite.wsAvailable('mod_survey_get_surveys_by_courses') && - $mmSite.wsAvailable('mod_survey_submit_answers'); + self.isPluginEnabled = function(siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.wsAvailable('mod_survey_get_questions') && + site.wsAvailable('mod_survey_get_surveys_by_courses') && + site.wsAvailable('mod_survey_submit_answers'); + }); }; /** diff --git a/www/addons/mod_url/main.js b/www/addons/mod_url/main.js index 387556708a7..3823f883df3 100644 --- a/www/addons/mod_url/main.js +++ b/www/addons/mod_url/main.js @@ -34,6 +34,9 @@ angular.module('mm.addons.mod_url', ['mm.core']) }) -.config(function($mmCourseDelegateProvider) { - $mmCourseDelegateProvider.registerContentHandler('mmaModUrl', 'url', '$mmaModUrlCourseContentHandler'); +.config(function($mmCourseDelegateProvider, $mmContentLinksDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModUrl', 'url', '$mmaModUrlHandlers.courseContentHandler'); + + // Register content links handler. + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModUrl', '$mmaModUrlHandlers.linksHandler'); }); diff --git a/www/addons/mod_url/services/course_content_handler.js b/www/addons/mod_url/services/course_content_handler.js deleted file mode 100644 index 717a4278af3..00000000000 --- a/www/addons/mod_url/services/course_content_handler.js +++ /dev/null @@ -1,75 +0,0 @@ -// (C) Copyright 2015 Martin Dougiamas -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -angular.module('mm.addons.mod_url') - -/** - * Mod URL course content handler. - * - * @module mm.addons.mod_url - * @ngdoc service - * @name $mmaModUrlCourseContentHandler - */ -.factory('$mmaModUrlCourseContentHandler', function($mmCourse, $mmaModUrl, $state) { - var self = {}; - - /** - * Whether or not the module is enabled for the site. - * - * @module mm.addons.mod_url - * @ngdoc method - * @name $mmaModUrlCourseContentHandler#isEnabled - * @return {Boolean} - */ - self.isEnabled = function() { - return true; - }; - - /** - * Get the controller. - * - * @module mm.addons.mod_url - * @ngdoc method - * @name $mmaModUrlCourseContentHandler#getController - * @param {Object} module The module info. - * @param {Number} courseid The course ID. - * @return {Function} - */ - self.getController = function(module, courseid) { - return function($scope) { - $scope.icon = $mmCourse.getModuleIconSrc('url'); - $scope.title = module.name; - $scope.action = function(e) { - $state.go('site.mod_url', {module: module, courseid: courseid}); - }; - - if (module.contents && module.contents[0] && module.contents[0].fileurl) { - $scope.buttons = [{ - icon: 'ion-link', - label: 'mm.core.openinbrowser', - action: function(e) { - e.preventDefault(); - e.stopPropagation(); - $mmaModUrl.logView(module.instance).then(function() { - $mmCourse.checkModuleCompletion(courseid, module.completionstatus); - }); - $mmaModUrl.open(module.contents[0].fileurl); - } - }]; - } - }; - }; - - return self; -}); diff --git a/www/addons/mod_url/services/handlers.js b/www/addons/mod_url/services/handlers.js new file mode 100644 index 00000000000..c0ea2e96933 --- /dev/null +++ b/www/addons/mod_url/services/handlers.js @@ -0,0 +1,134 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +angular.module('mm.addons.mod_url') + +/** + * Mod URL handlers. + * + * @module mm.addons.mod_url + * @ngdoc service + * @name $mmaModUrlHandlers + */ +.factory('$mmaModUrlHandlers', function($mmCourse, $mmaModUrl, $state, $mmUtil, $mmContentLinksHelper, $q) { + + var self = {}; + + /** + * Course content handler. + * + * @module mm.addons.mod_url + * @ngdoc method + * @name $mmaModUrlHandlers#courseContentHandler + */ + self.courseContentHandler = function() { + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @return {Boolean} + */ + self.isEnabled = function() { + return true; + }; + + /** + * Get the controller. + * + * @param {Object} module The module info. + * @param {Number} courseid The course ID. + * @return {Function} + */ + self.getController = function(module, courseid) { + return function($scope) { + $scope.icon = $mmCourse.getModuleIconSrc('url'); + $scope.title = module.name; + $scope.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $state.go('site.mod_url', {module: module, courseid: courseid}); + }; + + if (module.contents && module.contents[0] && module.contents[0].fileurl) { + $scope.buttons = [{ + icon: 'ion-link', + label: 'mm.core.openinbrowser', + action: function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $mmaModUrl.logView(module.instance).then(function() { + $mmCourse.checkModuleCompletion(courseid, module.completionstatus); + }); + $mmaModUrl.open(module.contents[0].fileurl); + } + }]; + } + }; + }; + + return self; + }; + + /** + * Content links handler. + * + * @module mm.addons.mod_url + * @ngdoc method + * @name $mmaModUrlHandlers#linksHandler + */ + self.linksHandler = function() { + + var self = {}; + + /** + * Whether or not the handler is enabled for a certain site. + * + * @param {String} siteId Site ID. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with true if enabled. + */ + function isEnabled(siteId, courseId) { + if (courseId) { + return $q.when(true); + } + return $mmCourse.canGetModuleWithoutCourseId(siteId); + } + + /** + * Get actions to perform with the link. + * + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with the list of actions. + * See {@link $mmContentLinksDelegate#registerLinkHandler}. + */ + self.getActions = function(siteIds, url, courseId) { + // Check it's a mod_url URL. + if (url.indexOf('/mod/url/view.php') > -1) { + return $mmContentLinksHelper.treatModuleIndexUrl(siteIds, url, isEnabled, courseId); + } + return $q.when([]); + }; + + return self; + }; + + return self; +}); diff --git a/www/addons/notifications/directives/actions.js b/www/addons/notifications/directives/actions.js index 85409c6fe65..f4fcd9e3160 100644 --- a/www/addons/notifications/directives/actions.js +++ b/www/addons/notifications/directives/actions.js @@ -27,7 +27,9 @@ angular.module('mm.addons.notifications') // Directive link function. function link(scope) { if (scope.contexturl) { - scope.actions = $mmContentLinksDelegate.getActionsFor(scope.contexturl, scope.courseid); + $mmContentLinksDelegate.getActionsFor(scope.contexturl, scope.courseid).then(function(actions) { + scope.actions = actions; + }); } } diff --git a/www/addons/participants/services/handlers.js b/www/addons/participants/services/handlers.js index 75f03627bd4..377ca066335 100644 --- a/www/addons/participants/services/handlers.js +++ b/www/addons/participants/services/handlers.js @@ -93,22 +93,14 @@ angular.module('mm.addons.participants') var self = {}; - /** - * Whether or not the handler is enabled for the site. - * - * @return {Boolean} - */ - self.isEnabled = function() { - return true; - }; - /** * Get actions to perform with the link. * - * @param {String} url URL to treat. - * @return {Object[]} List of actions. See {@link $mmContentLinksDelegate#registerLinkHandler}. + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @return {Object[]} List of actions. See {@link $mmContentLinksDelegate#registerLinkHandler}. */ - self.getActions = function(url) { + self.getActions = function(siteIds, url) { // Check it's a user URL. if (url.indexOf('grade/report/user') == -1 && url.indexOf('/user/index.php') > -1) { var params = $mmUtil.extractUrlParams(url); @@ -117,6 +109,7 @@ angular.module('mm.addons.participants') return [{ message: 'mm.core.view', icon: 'ion-eye', + sites: siteIds, action: function(siteId) { var stateParams = { course: {id: parseInt(params.id, 10)} diff --git a/www/core/components/contentlinks/controllers/choosesite.js b/www/core/components/contentlinks/controllers/choosesite.js index fbcb3384c87..b13405a21b5 100644 --- a/www/core/components/contentlinks/controllers/choosesite.js +++ b/www/core/components/contentlinks/controllers/choosesite.js @@ -21,11 +21,13 @@ angular.module('mm.core.contentlinks') * @ngdoc controller * @name mmContentLinksChooseSiteCtrl */ -.controller('mmContentLinksChooseSiteCtrl', function($scope, $stateParams, $mmSitesManager, $mmUtil, $ionicHistory, $state, - $mmContentLinksDelegate) { +.controller('mmContentLinksChooseSiteCtrl', function($scope, $stateParams, $mmSitesManager, $mmUtil, $ionicHistory, $state, $q, + $mmContentLinksDelegate, $mmContentLinksHelper) { $scope.url = $stateParams.url || ''; + var action; + function leaveView() { $mmSitesManager.logout().finally(function() { $ionicHistory.nextViewOptions({ @@ -41,33 +43,22 @@ angular.module('mm.core.contentlinks') return; } - $mmSitesManager.getSiteIdsFromUrl($scope.url, false).then(function(ids) { - if (!ids.length) { - $mmUtil.showErrorModal('mm.contentlinks.errornosites', true); - leaveView(); - return; + $mmContentLinksDelegate.getActionsFor($scope.url).then(function(actions) { + action = $mmContentLinksHelper.getFirstValidAction(actions); + if (!action) { + return $q.reject(); } - $mmSitesManager.getSites(ids).then(function(sites) { + $mmSitesManager.getSites(action.sites).then(function(sites) { $scope.sites = sites; }); + }).catch(function() { + $mmUtil.showErrorModal('mm.contentlinks.errornosites', true); + leaveView(); }); $scope.siteClicked = function(siteId) { - // Get actions to treat the link. - var actions = $mmContentLinksDelegate.getActionsFor($scope.url); - if (actions && actions.length) { - for (var i = 0; i < actions.length; i++) { - if (actions[i] && angular.isFunction(actions[i].action)) { - actions[i].action(siteId); - return; - } - } - } - - // No action found. - $mmUtil.showErrorModal('mm.contentlinks.errornoactions', true); - $scope.cancel(); + action.action(siteId); }; $scope.cancel = function() { diff --git a/www/core/components/contentlinks/services/delegate.js b/www/core/components/contentlinks/services/delegate.js index 278e0f45e1e..2c7f970ba9a 100644 --- a/www/core/components/contentlinks/services/delegate.js +++ b/www/core/components/contentlinks/services/delegate.js @@ -35,12 +35,11 @@ angular.module('mm.core.contentlinks') * @param {String} name Handler's name. * @param {String|Object|Function} handler Must be resolved to an object defining the following functions. Or to a function * returning an object defining these functions. See {@link $mmUtil#resolveObject}. - * - isEnabled (Boolean|Promise) Whether or not the handler is enabled on a site level. - * When using a promise, it should return a boolean. - * - getActions(url, courseid) (Object[]) Returns list of actions. Each action must have: + * - getActions(siteIds, url, courseId) (Promise) Returns list of actions. Each action must have: * - message: Message related to the action to do. E.g. 'View'. * - icon: Icon related to the action to do. - * - action: A function to be called when the link is clicked. + * - sites: Sites IDs that support the action. Subset of 'siteIds'. + * - action(siteId): A function to be called when the link is clicked. * @param {Number} [priority] Handler's priority. */ self.registerLinkHandler = function(name, handler, priority) { @@ -59,9 +58,8 @@ angular.module('mm.core.contentlinks') return true; }; - self.$get = function($mmUtil, $log, $mmSite, $q) { - var enabledLinkHandlers = {}, - self = {}; + self.$get = function($mmUtil, $log, $q, $mmSitesManager) { + var self = {}; $log = $log.getInstance('$mmContentLinksDelegate'); @@ -74,122 +72,66 @@ 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. - * @return {Object[]} Actions. See {@link $mmContentLinksDelegate#registerLinkHandler}. + * @return {Promise} Promise resolved with the actions. See {@link $mmContentLinksDelegate#registerLinkHandler}. */ self.getActionsFor = function(url, courseId) { if (!url) { - return []; + return $q.when([]); } - var linkActions = {}; - angular.forEach(enabledLinkHandlers, function(handler) { - if (handler.instance && angular.isFunction(handler.instance.getActions)) { - var actions = handler.instance.getActions(url, courseId); - if (actions && actions.length) { - linkActions[handler.priority] = actions; + // Get the list of sites the URL belongs to. + return $mmSitesManager.getSiteIdsFromUrl(url, true).then(function(siteIds) { + var linkActions = [], + promises = []; + + angular.forEach(linkHandlers, function(handler) { + if (typeof handler.instance === 'undefined') { + handler.instance = $mmUtil.resolveObject(handler.handler, true); } - } - }); - return sortActionsByPriority(linkActions); - }; - /** - * Converts an object with priority -> action to an array of actions ordered by priority. - * If object keys are numbers they're usually automatically ordered, but we can't be 100% sure. - * - * @param {Object} actions Actions to sort. - * @return {Object[]} Sorted actions. - */ - function sortActionsByPriority(actions) { - var sorted = [], - priorities = Object.keys(actions); - // Sort priorities. - priorities = priorities.sort(function(a, b) { - return parseInt(a, 10) > parseInt(b, 10); - }); - // Fill sorted array. - priorities.forEach(function(priority) { - var list = actions[priority]; - list.forEach(function(action) { - sorted.push(action); + if (handler.instance) { + promises.push($q.when(handler.instance.getActions(siteIds, url, courseId)).then(function(actions) { + if (actions && actions.length) { + linkActions.push({ + priority: handler.priority, + actions: actions + }); + } + })); + } }); - }); - return sorted; - } - /** - * Update the enabled link handlers for the current site. - * - * @module mm.core.contentlinks - * @ngdoc method - * @name $mmContentLinksDelegate#updateLinkHandler - * @param {String} name The handler name. - * @param {Object} handlerInfo The handler details. - * @return {Promise} Resolved when enabled, rejected when not. - * @protected - */ - self.updateLinkHandler = function(name, handlerInfo) { - var promise; - - if (typeof handlerInfo.instance === 'undefined') { - handlerInfo.instance = $mmUtil.resolveObject(handlerInfo.handler, true); - } - - if (!$mmSite.isLoggedIn()) { - promise = $q.reject(); - } else { - promise = $q.when(handlerInfo.instance.isEnabled()); - } - - // Checks if the content is enabled. - return promise.then(function(enabled) { - if (enabled) { - enabledLinkHandlers[name] = { - instance: handlerInfo.instance, - priority: handlerInfo.priority - }; - } else { - return $q.reject(); - } - }).catch(function() { - delete enabledLinkHandlers[name]; + return $mmUtil.allPromises(promises).catch(function() {}).then(function() { + // Sort link actions by priority. + return sortActionsByPriority(linkActions); + }); }); }; /** - * Update the link handlers for the current site. + * 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. * - * @module mm.core.contentlinks - * @ngdoc method - * @name $mmContentLinksDelegate#updateLinkHandlers - * @return {Promise} Resolved when done. - * @protected + * @param {Object[]} actions Actions to sort. + * @return {Object[]} Sorted actions. */ - self.updateLinkHandlers = function() { - var promises = []; - - $log.debug('Updating link handlers for current site.'); + function sortActionsByPriority(actions) { + var sorted = []; - // Loop over all the link handlers. - angular.forEach(linkHandlers, function(handler, name) { - promises.push(self.updateLinkHandler(name, handler)); + // Sort by priority. + actions = actions.sort(function(a, b) { + return a.priority > b.priority; }); - return $q.all(promises).then(function() { - return true; - }, function() { - // Never reject. - return true; + // Fill result array. + actions.forEach(function(entry) { + sorted = sorted.concat(entry.actions); }); - }; + return sorted; + } return self; }; return self; -}) - -.run(function($mmEvents, $mmContentLinksDelegate, mmCoreEventLogin, mmCoreEventSiteUpdated) { - $mmEvents.on(mmCoreEventLogin, $mmContentLinksDelegate.updateLinkHandlers); - $mmEvents.on(mmCoreEventSiteUpdated, $mmContentLinksDelegate.updateLinkHandlers); }); diff --git a/www/core/components/contentlinks/services/helper.js b/www/core/components/contentlinks/services/helper.js index c07c2ff06bd..5f56e4aa091 100644 --- a/www/core/components/contentlinks/services/helper.js +++ b/www/core/components/contentlinks/services/helper.js @@ -21,20 +21,82 @@ angular.module('mm.core.contentlinks') * @ngdoc service * @name $mmContentLinksHelper */ -.factory('$mmContentLinksHelper', function($log, $ionicHistory, $state, $mmSite, $mmSitesManager, $mmContentLinksDelegate, $q, - $mmUtil, $translate) { +.factory('$mmContentLinksHelper', function($log, $ionicHistory, $state, $mmSite, $mmContentLinksDelegate, $mmUtil, $translate, + $mmCourseHelper) { $log = $log.getInstance('$mmContentLinksHelper'); var self = {}; + /** + * Filter the list of supported sites based on a isEnabled function. + * + * @module mm.core.contentlinks + * @ngdoc method + * @name $mmContentLinksHelper#filterSupportedSites + * @param {String[]} siteIds Site IDs to filter. + * @param {Function} isEnabledFn Function to call for each site. Must return a promise resolved with true if enabled. It + * receives a siteId param and all the params sent to this function after 'checkAll'. + * @param {Boolean} checkAll True if it should check all the sites, false if it should check only 1 and treat them all + * depending on this result. + * @param {Mixed} All the params sent after checkAll will be passed to isEnabledFn. + * @return {Promise} Promise resolved with the list of supported sites. + */ + self.filterSupportedSites = function(siteIds, isEnabledFn, checkAll) { + var promises = [], + supported = [], + extraParams = Array.prototype.slice.call(arguments, 3); // Params received after 'checkAll'. + + angular.forEach(siteIds, function(siteId) { + if (checkAll || !promises.length) { + promises.push(isEnabledFn.apply(isEnabledFn, [siteId].concat(extraParams)).then(function(enabled) { + if (enabled) { + supported.push(siteId); + } + })); + } + }); + + return $mmUtil.allPromises(promises).catch(function() {}).then(function() { + if (!checkAll) { + if (supported.length) { + return siteIds; // Checking 1 was enough and it succeeded, all sites supported. + } else { + return []; // Checking 1 was enough and it failed, no sites supported. + } + } else { + return supported; + } + }); + }; + + /** + * Get the first valid action in a list of actions. + * + * @module mm.core.contentlinks + * @ngdoc method + * @name $mmContentLinksHelper#getFirstValidAction + * @param {Object[]} actions List of actions. + * @return {Object} First valid action. Returns undefined if no valid action found. + */ + self.getFirstValidAction = function(actions) { + if (actions) { + for (var i = 0; i < actions.length; i++) { + var action = actions[i]; + if (action && action.sites && action.sites.length && angular.isFunction(action.action)) { + return action; + } + } + } + }; + /** * Goes to a certain state in a certain site. If the site is current site it will perform a regular navigation, * otherwise it uses the 'redirect' state to change the site. * - * @module mm.core + * @module mm.core.contentlinks * @ngdoc method - * @name $mmUtil#goToSite + * @name $mmContentLinksHelper#goInSite * @param {String} stateName Name of the state to go. * @param {Object} stateParams Params to send to the state. * @param {String} [siteId] Site ID. If not defined, current site. @@ -79,43 +141,63 @@ angular.module('mm.core.contentlinks') * @return {Promise} Promise resolved with a boolean: true if URL was treated, false otherwise. */ self.handleLink = function(url) { - // Check if the link should be treated by some component/addon. - // We perform this check first because it's synchronous. - var actions = $mmContentLinksDelegate.getActionsFor(url); - if (actions && actions.length) { - for (var i = 0; i < actions.length; i++) { - var action = actions[i]; - if (action && angular.isFunction(action.action)) { - - // We found a valid action. We need to check if the link belongs to any site stored. - return $mmSitesManager.getSiteIdsFromUrl(url, true).then(function(ids) { - if (!ids.length) { - // URL doesn't belong to any site. - return false; - } else if (ids.length == 1 && ids[0] == $mmSite.getId()) { - // Current site. - action.action(ids[0]); + return $mmContentLinksDelegate.getActionsFor(url).then(function(actions) { + var action = self.getFirstValidAction(actions); + if (action) { + if (action.sites.length == 1 && action.sites[0] == $mmSite.getId()) { + // Current site. + action.action(action.sites[0]); + } else { + // Not current site or more than one site. Ask for confirmation. + $mmUtil.showConfirm($translate('mm.contentlinks.confirmurlothersite')).then(function() { + if (action.sites.length == 1) { + action.action(action.sites[0]); } else { - // Not current site. Ask for confirmation. - $mmUtil.showConfirm($translate('mm.contentlinks.confirmurlothersite')).then(function() { - if (ids.length == 1) { - action.action(ids[0]); - } else { - self.goToChooseSite(url); - } - }); + self.goToChooseSite(url); } - return true; - }).catch(function() { - return false; }); } + return true; } - } + }).catch(function() { + return false; + }); + }; - // No valid actions found. - return $q.when(false); + /** + * Treats a URL that belongs to a module's index page. + * + * @module mm.core.contentlinks + * @ngdoc method + * @name $mmContentLinksHelper#treatModuleIndexUrl + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @param {Function} isEnabled Function to check if the module is enabled. @see $mmContentLinksHelper#filterSupportedSites . + * @param {Number} [courseId] Course ID related to the URL. + * @return {Promise} Promise resolved with the list of actions. + */ + self.treatModuleIndexUrl = function(siteIds, url, isEnabled, courseId) { + var params = $mmUtil.extractUrlParams(url); + if (typeof params.id != 'undefined') { + // Pass false because all sites should have the same siteurl. + return self.filterSupportedSites(siteIds, isEnabled, false, courseId).then(function(ids) { + if (!ids.length) { + return []; + } else { + // Return actions. + return [{ + message: 'mm.core.view', + icon: 'ion-eye', + sites: ids, + action: function(siteId) { + $mmCourseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId); + } + }]; + } + }); + } + return $q.when([]); }; return self; diff --git a/www/core/components/course/controllers/section.js b/www/core/components/course/controllers/section.js index f1519a9d98f..cb795bf6d35 100644 --- a/www/core/components/course/controllers/section.js +++ b/www/core/components/course/controllers/section.js @@ -22,16 +22,17 @@ angular.module('mm.core.course') * @name mmCourseSectionCtrl */ .controller('mmCourseSectionCtrl', function($mmCourseDelegate, $mmCourse, $mmUtil, $scope, $stateParams, $translate, $mmSite, - $mmEvents, $ionicScrollDelegate, $mmCourses, $q, mmCoreEventCompletionModuleViewed) { + $mmEvents, $ionicScrollDelegate, $mmCourses, $q, mmCoreEventCompletionModuleViewed, $controller) { // Default values are course 1 (front page) and all sections. - var courseid = $stateParams.courseid || 1, - sectionid = $stateParams.sectionid || -1; + var courseId = $stateParams.cid || 1, + sectionId = $stateParams.sectionid || -1, + moduleId = $stateParams.mid; - $scope.sitehome = (courseid === 1); // Are we visiting the site home? + $scope.sitehome = (courseId === 1); // Are we visiting the site home? $scope.sections = []; // Reset scope.sections, otherwise an error is shown in console with tablet view. - if (sectionid < 0) { + if (sectionId < 0) { // Special scenario, we want all sections. if ($scope.sitehome) { $scope.title = $translate.instant('mma.frontpage.sitehome'); @@ -42,15 +43,15 @@ angular.module('mm.core.course') } // Convenience function to fetch section(s). - function loadContent(sectionid) { - return $mmCourses.getUserCourse(courseid, true).catch(function() { + function loadContent(sectionId) { + return $mmCourses.getUserCourse(courseId, true).catch(function() { // User not enrolled in the course or an error occurred, ignore the error. }).then(function(course) { var promise; if (course && course.enablecompletion === false) { promise = $q.when([]); // Completion not enabled, return empty array. } else { - promise = $mmCourse.getActivitiesCompletionStatus(courseid).catch(function() { + promise = $mmCourse.getActivitiesCompletionStatus(courseId).catch(function() { return []; // If fail, return empty array (as if there was no completion). }); } @@ -59,12 +60,12 @@ angular.module('mm.core.course') var promise, sectionnumber; - if (sectionid < 0) { + if (sectionId < 0) { sectionnumber = 0; - promise = $mmCourse.getSections(courseid); + promise = $mmCourse.getSections(courseId); } else { - sectionnumber = sectionid; - promise = $mmCourse.getSection(courseid, sectionid).then(function(section) { + sectionnumber = sectionId; + promise = $mmCourse.getSection(courseId, sectionId).then(function(section) { $scope.title = section.name; $scope.summary = section.summary; return [section]; @@ -86,12 +87,21 @@ angular.module('mm.core.course') angular.forEach(section.modules, function(module) { module._controller = - $mmCourseDelegate.getContentHandlerControllerFor(module.modname, module, courseid, section.id); + $mmCourseDelegate.getContentHandlerControllerFor(module.modname, module, courseId, section.id); // Check if activity has completions and if it's marked. var status = statuses[module.id]; if (typeof status != 'undefined') { module.completionstatus = status; } + + if (module.id == moduleId) { + // This is the module we're looking for. Open it. + var scope = $scope.$new(); + $controller(module._controller, {$scope: scope}); + if (scope.action) { + scope.action(); + } + } }); }); @@ -100,7 +110,7 @@ angular.module('mm.core.course') // Add log in Moodle. $mmSite.write('core_course_view_course', { - courseid: courseid, + courseid: courseId, sectionnumber: sectionnumber }); }, function(error) { @@ -114,13 +124,13 @@ angular.module('mm.core.course') }); } - loadContent(sectionid).finally(function() { + loadContent(sectionId).finally(function() { $scope.sectionLoaded = true; }); $scope.doRefresh = function() { - $mmCourse.invalidateSections(courseid).finally(function() { - loadContent(sectionid).finally(function() { + $mmCourse.invalidateSections(courseId).finally(function() { + loadContent(sectionId).finally(function() { $scope.$broadcast('scroll.refreshComplete'); }); }); @@ -134,7 +144,7 @@ angular.module('mm.core.course') } $scope.sectionLoaded = false; $scope.sections = []; - loadContent(sectionid).finally(function() { + loadContent(sectionId).finally(function() { $scope.sectionLoaded = true; $scope.loadingPaddingTop = 0; }); @@ -142,14 +152,14 @@ angular.module('mm.core.course') // Completion changed for at least one module. Invalidate data and re-load it. $scope.completionChanged = function() { - $mmCourse.invalidateSections(courseid).finally(function() { + $mmCourse.invalidateSections(courseId).finally(function() { refreshAfterCompletionChange(); }); }; // Listen for viewed modules. If an automatic completion module is viewed, refresh the whole list. var observer = $mmEvents.on(mmCoreEventCompletionModuleViewed, function(cid) { - if (cid === courseid) { + if (cid === courseId) { refreshAfterCompletionChange(); } }); diff --git a/www/core/components/course/controllers/sections.js b/www/core/components/course/controllers/sections.js index c436048a595..0d4094cb7e7 100644 --- a/www/core/components/course/controllers/sections.js +++ b/www/core/components/course/controllers/sections.js @@ -23,11 +23,14 @@ angular.module('mm.core.course') */ .controller('mmCourseSectionsCtrl', function($mmCourse, $mmUtil, $scope, $stateParams, $translate, $mmCourseHelper, $mmEvents, $mmSite, $mmCoursePrefetchDelegate, $mmCourses, $q, $ionicHistory, $ionicPlatform, mmCoreCourseAllSectionsId, - mmCoreEventSectionStatusChanged, $mmConfig, mmCoreSettingsDownloadSection) { - var courseid = $stateParams.courseid, + mmCoreEventSectionStatusChanged, $mmConfig, mmCoreSettingsDownloadSection, $state, $timeout) { + var courseId = $stateParams.courseid, + sectionId = $stateParams.sid, + moduleId = $stateParams.moduleid, downloadSectionsEnabled; - $scope.courseid = courseid; + $scope.courseId = courseId; + $scope.sectionToLoad = 2; // Load "General" section by default. function checkDownloadSectionsEnabled() { return $mmConfig.get(mmCoreSettingsDownloadSection, true).then(function(enabled) { @@ -40,10 +43,10 @@ angular.module('mm.core.course') function loadSections(refresh) { // Get full course data. If not refreshing we'll try to get it from cache to speed up the response. - return $mmCourses.getUserCourse(courseid).then(function(course) { + return $mmCourses.getUserCourse(courseId).then(function(course) { $scope.fullname = course.fullname; // Get the sections. - return $mmCourse.getSections(courseid).then(function(sections) { + return $mmCourse.getSections(courseId).then(function(sections) { // Add a fake first section (all sections). return $translate('mm.course.allsections').then(function(str) { // Adding fake first section. @@ -56,7 +59,7 @@ angular.module('mm.core.course') if (downloadSectionsEnabled) { // Calculate status of the sections. - return $mmCourseHelper.calculateSectionsStatus(result, courseid, true, refresh).catch(function() { + return $mmCourseHelper.calculateSectionsStatus(result, courseId, true, refresh).catch(function() { // Ignore errors (shouldn't happen). }).then(function(downloadpromises) { // If we restored any download we'll recalculate the status once all of them have finished. @@ -68,7 +71,7 @@ angular.module('mm.core.course') }).finally(function() { if (!$scope.$$destroyed) { // Recalculate the status. - $mmCourseHelper.calculateSectionsStatus($scope.sections, courseid, false); + $mmCourseHelper.calculateSectionsStatus($scope.sections, courseId, false); } }); } @@ -88,7 +91,7 @@ angular.module('mm.core.course') // Prefetch a section. The second parameter indicates if the prefetch was started manually (true) // or it was automatically started because all modules are being downloaded (false). function prefetch(section, manual) { - $mmCourseHelper.prefetch(section, courseid, $scope.sections).catch(function() { + $mmCourseHelper.prefetch(section, courseId, $scope.sections).catch(function() { // Don't show error message if scope is destroyed or it's an automatic download but we aren't in this state. if ($scope.$$destroyed) { return; @@ -105,15 +108,41 @@ angular.module('mm.core.course') }).finally(function() { if (!$scope.$$destroyed) { // Recalculate the status. - $mmCourseHelper.calculateSectionsStatus($scope.sections, courseid, false); + $mmCourseHelper.calculateSectionsStatus($scope.sections, courseId, false); } }); } + // Convenience function to autoload a section if sectionId param is set. + function autoloadSection() { + if (sectionId) { + if ($ionicPlatform.isTablet()) { + // Search the position of the section to load. + angular.forEach($scope.sections, function(section, index) { + if (section.id == sectionId) { + $scope.sectionToLoad = index + 1; + } + }); + // Set moduleId to pass it to the new state when the section is autoloaded. We unset it after this + // to prevent autoloading the module when the user manually loads a section. + $scope.moduleId = moduleId; + $timeout(function() { + $scope.moduleId = null; // Unset moduleId when + }, 500); + } else { + $state.go('site.mm_course-section', { + sectionid: sectionId, + cid: courseId, + mid: moduleId + }); + } + } + } + $scope.doRefresh = function() { var promises = []; promises.push($mmCourses.invalidateUserCourses()); - promises.push($mmCourse.invalidateSections(courseid)); + promises.push($mmCourse.invalidateSections(courseId)); $q.all(promises).finally(function() { loadSections(true).finally(function() { @@ -126,13 +155,14 @@ angular.module('mm.core.course') e.preventDefault(); e.stopPropagation(); - $mmCourseHelper.confirmDownloadSize(courseid, section, $scope.sections).then(function() { + $mmCourseHelper.confirmDownloadSize(courseId, section, $scope.sections).then(function() { prefetch(section, true); }); }; checkDownloadSectionsEnabled().then(function() { loadSections().finally(function() { + autoloadSection(); $scope.sectionsLoaded = true; }); }); @@ -148,7 +178,7 @@ angular.module('mm.core.course') } // Recalculate the status. - $mmCourseHelper.calculateSectionsStatus($scope.sections, courseid, false).then(function() { + $mmCourseHelper.calculateSectionsStatus($scope.sections, courseId, false).then(function() { var section; angular.forEach($scope.sections, function(s) { if (s.id === data.sectionid) { diff --git a/www/core/components/course/lang/en.json b/www/core/components/course/lang/en.json index cbe906345d2..78449400ca6 100644 --- a/www/core/components/course/lang/en.json +++ b/www/core/components/course/lang/en.json @@ -8,6 +8,7 @@ "couldnotloadsections": "Could not load the sections, please try again later.", "couldnotloadsectioncontent": "Could not load the section content, please try again later.", "errordownloadingsection": "Error downloading section.", + "errorgetmodule": "Error getting module data.", "gotothesite": "Go to the site", "nocontentavailable": "No content available at the moment.", "showall": "Show all", diff --git a/www/core/components/course/main.js b/www/core/components/course/main.js index 21e990bf2cd..db81e80bf4d 100644 --- a/www/core/components/course/main.js +++ b/www/core/components/course/main.js @@ -24,7 +24,9 @@ angular.module('mm.core.course', ['mm.core.courses']) .state('site.mm_course', { url: '/mm_course', params: { - courseid: null + courseid: null, + sid: null, // Section to load. Not naming it sectionid because it collides with 'mm_course-section' param in split-view. + moduleid: null // Module to load. }, views: { 'site': { @@ -38,7 +40,8 @@ angular.module('mm.core.course', ['mm.core.courses']) url: '/mm_course-section', params: { sectionid: null, - courseid: null + cid: null, // Not naming it courseid because it collides with 'site.mm_course' param in split-view. + mid: null // Not naming it moduleid because it collides with 'site.mm_course' param in split-view. }, views: { 'site': { diff --git a/www/core/components/course/services/course.js b/www/core/components/course/services/course.js index d8028c3eb9e..cbbf61878b9 100644 --- a/www/core/components/course/services/course.js +++ b/www/core/components/course/services/course.js @@ -33,7 +33,7 @@ angular.module('mm.core.course') * @ngdoc service * @name $mmCourse */ -.factory('$mmCourse', function($mmSite, $translate, $q, $log, $mmEvents, mmCoreEventCompletionModuleViewed) { +.factory('$mmCourse', function($mmSite, $translate, $q, $log, $mmEvents, $mmSitesManager, mmCoreEventCompletionModuleViewed) { $log = $log.getInstance('$mmCourse'); @@ -58,6 +58,40 @@ angular.module('mm.core.course') return module; } + /** + * Check if the site is prepared to return a module without having its course ID. + * + * @module mm.core.course + * @ngdoc method + * @name $mmCourse#canGetModuleWithoutCourseId + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with true if can return it, rejected or resolved with false otherwise. + */ + self.canGetModuleWithoutCourseId = function(siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.wsAvailable('core_course_get_course_module'); + }); + }; + + /** + * Check if the site is prepared to return a module by instance ID. + * + * @module mm.core.course + * @ngdoc method + * @name $mmCourse#canGetModuleByInstance + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with true if can return it, rejected or resolved with false otherwise. + */ + self.canGetModuleByInstance = function(siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.wsAvailable('core_course_get_course_module_by_instance'); + }); + }; + /** * Check if module completion could have changed. If it could have, trigger event. This function must be used, * for example, after calling a "module_view" WS since it can change the module completion. @@ -122,63 +156,155 @@ angular.module('mm.core.course') return 'mmCourse:activitiescompletion:' + courseid + ':' + userid; } + /** + * Gets a module basic info by module ID. + * + * @module mm.core.course + * @ngdoc method + * @name $mmCourse#getModuleBasicInfo + * @param {Number} moduleId Module ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with the module's info. + */ + self.getModuleBasicInfo = function(moduleId, siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + var params = { + cmid: moduleId + }, + preSets = { + cacheKey: getModuleCacheKey(moduleId) + }; + + return site.read('core_course_get_course_module', params, preSets).then(function(response) { + if (response.cm && (!response.warnings || !response.warnings.length)) { + return response.cm; + } + return $q.reject(); + }); + }); + }; + + /** + * Gets a module basic info by instance. + * + * @module mm.core.course + * @ngdoc method + * @name $mmCourse#getModuleBasicInfoByInstance + * @param {Number} id Instance ID. + * @param {String} module Name of the module. E.g. 'glossary'. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with the module's info. + */ + self.getModuleBasicInfoByInstance = function(id, module, siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmSitesManager.getSite(siteId).then(function(site) { + var params = { + instance: id, + module: module + }, + preSets = { + cacheKey: getModuleByInstanceCacheKey(id, module) + }; + + return site.read('core_course_get_course_module_by_instance', params, preSets).then(function(response) { + if (response.cm && (!response.warnings || !response.warnings.length)) { + return response.cm; + } + return $q.reject(); + }); + }); + }; + /** * Get a module from Moodle. * * @module mm.core.course * @ngdoc method * @name $mmCourse#getModule - * @param {Number} courseid The course ID. - * @param {Number} moduleid The module ID. - * @param {Number} [sectionid] The section ID. + * @param {Number} moduleId The module ID. + * @param {Number} [courseId] The course ID. Recommended to speed up the process and minimize data usage. + * @param {Number} [sectionId] The section ID. * @return {Promise} */ - self.getModule = function(courseid, moduleid, sectionid) { + self.getModule = function(moduleId, courseId, sectionId) { - if (!moduleid) { + if (!moduleId) { return $q.reject(); } - $log.debug('Getting module ' + moduleid + ' in course ' + courseid + ' and section ' +sectionid); + var promise; - var params = { - courseid: courseid, + if (!courseId) { + // No courseId passed, try to retrieve it. + promise = self.getModuleBasicInfo(moduleId).then(function(module) { + return module.course; + }); + } else { + promise = $q.when(courseId); + } + + return promise.then(function(courseId) { + // We have courseId, we can use core_course_get_contents for compatibility. + $log.debug('Getting module ' + moduleId + ' in course ' + courseId); + + params = { + courseid: courseId, options: [ { name: 'cmid', - value: moduleid + value: moduleId } ] - }, + }; preSets = { - cacheKey: getModuleCacheKey(moduleid) + cacheKey: getModuleCacheKey(moduleId) }; - if (sectionid) { - params.options.push({ - name: 'sectionid', - value: sectionid - }); - } - - return $mmSite.read('core_course_get_contents', params, preSets).then(function(sections) { - var section, - module; + if (sectionId) { + params.options.push({ + name: 'sectionid', + value: sectionId + }); + } - for (var i = 0; i < sections.length; i++) { - section = sections[i]; - for (var j = 0; j < section.modules.length; j++) { - module = section.modules[j]; - if (module.id === moduleid) { - return addContentsIfNeeded(module); + return $mmSite.read('core_course_get_contents', params, preSets).catch(function() { + // Error getting the module. Try to get all contents (without filtering). + params.options = []; + preSets.cacheKey = getSectionsCacheKey(courseId); + return $mmSite.read('core_course_get_contents', params, preSets); + }).then(function(sections) { + var section, + module; + + for (var i = 0; i < sections.length; i++) { + section = sections[i]; + for (var j = 0; j < section.modules.length; j++) { + module = section.modules[j]; + if (module.id == moduleId) { + module.course = courseId; + return addContentsIfNeeded(module); + } } } - } - - return $q.reject(); + return $q.reject(); + }); }); }; + /** + * Get cache key for module WS calls. + * + * @param {Number} id Instance ID. + * @param {String} module Name of the module. E.g. 'glossary'. + * @return {String} Cache key. + */ + function getModuleByInstanceCacheKey(id, module) { + return 'mmCourse:moduleByInstance:' + module + ':' + id; + } + /** * Get cache key for module WS calls. * @@ -206,6 +332,47 @@ angular.module('mm.core.course') return "img/mod/" + moduleName + ".svg"; }; + /** + * Get the section ID a module belongs to. + * + * @module mm.core.course + * @ngdoc method + * @name $mmCourse#getModuleSectionId + * @param {Number} moduleId The module ID. + * @param {Number} [courseId] The course ID. Required if Moodle site is prior to 3.0. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} + */ + self.getModuleSectionId = function(moduleId, courseId, siteId) { + + if (!moduleId) { + return $q.reject(); + } + + // Try to get the section using getModuleBasicInfo. + return self.getModuleBasicInfo(moduleId, siteId).then(function(module) { + return module.section; + }).catch(function() { + if (!courseId) { + // It failed and we don't have courseId, reject. + return $q.reject(); + } + + // 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) { + return section.id; + } + }); + }); + // Not found. + return $q.reject(); + }); + }); + }; + /** * Return a specific section. * @@ -247,22 +414,26 @@ angular.module('mm.core.course') * @name $mmCourse#getSections * @param {Number} courseid The course ID. * @param {Object} [preSets] Optional. Presets to use. + * @param {String} [siteId] Site ID. If not defined, current site. * @return {Promise} The reject contains the error message, else contains the sections. */ - self.getSections = function(courseid, preSets) { + self.getSections = function(courseid, preSets, siteId) { preSets = preSets || {}; + siteId = siteId || $mmSite.getId(); preSets.cacheKey = getSectionsCacheKey(courseid); - return $mmSite.read('core_course_get_contents', { - courseid: courseid, - options: [] - }, preSets).then(function(sections) { - angular.forEach(sections, function(section) { - angular.forEach(section.modules, function(module) { - addContentsIfNeeded(module); + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.read('core_course_get_contents', { + courseid: courseid, + options: [] + }, preSets).then(function(sections) { + angular.forEach(sections, function(section) { + angular.forEach(section.modules, function(module) { + addContentsIfNeeded(module); + }); }); + return sections; }); - return sections; }); }; @@ -289,6 +460,20 @@ angular.module('mm.core.course') return $mmSite.invalidateWsCacheForKey(getModuleCacheKey(moduleid)); }; + /** + * Invalidates module WS call. + * + * @module mm.core.course + * @ngdoc method + * @name $mmCourse#invalidateModuleByInstance + * @param {Number} id Instance ID. + * @param {String} module Name of the module. E.g. 'glossary'. + * @return {Promise} Promise resolved when the data is invalidated. + */ + self.invalidateModuleByInstance = function(id, module) { + return $mmSite.invalidateWsCacheForKey(getModuleByInstanceCacheKey(id, module)); + }; + /** * Invalidates sections WS call. * diff --git a/www/core/components/course/services/helper.js b/www/core/components/course/services/helper.js index ecd035b84cc..7db7d01c81c 100644 --- a/www/core/components/course/services/helper.js +++ b/www/core/components/course/services/helper.js @@ -21,9 +21,8 @@ angular.module('mm.core.course') * @ngdoc service * @name $mmCourseHelper */ -.factory('$mmCourseHelper', function($q, $mmCoursePrefetchDelegate, $mmApp, $mmFilepool, $mmUtil, $translate, $mmText, - mmCoreNotDownloaded, mmCoreOutdated, mmCoreDownloading, mmCoreWifiDownloadThreshold, mmCoreDownloadThreshold, - mmCoreCourseAllSectionsId) { +.factory('$mmCourseHelper', function($q, $mmCoursePrefetchDelegate, $mmFilepool, $mmUtil, $mmCourse, $mmSite, $state, + mmCoreNotDownloaded, mmCoreOutdated, mmCoreDownloading, mmCoreCourseAllSectionsId) { var self = {}; @@ -167,6 +166,30 @@ angular.module('mm.core.course') }); }; + /** + * Get the course ID from a module, showing an error message if it can't be retrieved. + * + * @module mm.core.course + * @ngdoc method + * @name $mmCourseHelper#getModuleCourseId + * @param {Number} id Instance ID. + * @param {String} module Name of the module. E.g. 'glossary'. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with the module's course ID. + */ + self.getModuleCourseIdByInstance = function(id, module, siteId) { + return $mmCourse.getModuleBasicInfoByInstance(id, module, siteId).then(function(cm) { + return cm.course; + }).catch(function(error) { + if (error) { + $mmUtil.showErrorModal(error); + } else { + $mmUtil.showErrorModal('mm.course.errorgetmodule', true); + } + return $q.reject(); + }); + }; + /** * Get the download ID of a section. It's used to interact with $mmCoursePrefetchDelegate. * @@ -180,6 +203,66 @@ angular.module('mm.core.course') return 'Section-'+section.id; }; + /** + * Retrieves the courseId of the module and navigates to it. + * + * @module mm.core.course + * @ngdoc method + * @name $mmCourseHelper#navigateToModule + * @param {Number} moduleId Module's ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @param {Number} [courseId] Course ID. If not defined we'll try to retrieve it from the site. + * @param {Number} [sectionId] Section the module belongs to. If not defined we'll try to retrieve it from the site. + * @return {Promise} Promise resolved when the state changes. + */ + self.navigateToModule = function(moduleId, siteId, courseId, sectionId) { + siteId = siteId || $mmSite.getId(); + var modal = $mmUtil.showModalLoading(), + promise; + + return $mmCourse.canGetModuleWithoutCourseId(siteId).then(function(enabled) { + if (courseId && sectionId) { + // No need to retrieve more data. + promise = $q.when(); + } else if (!courseId && !enabled) { + // We don't have enough data and we can't retrieve it. + promise = $q.reject(); + } else if (!courseId) { + // We don't have courseId but WS is enabled. + promise = $mmCourse.getModuleBasicInfo(moduleId, siteId).then(function(module) { + courseId = module.course; + sectionId = module.section; + }); + } else { + // We don't have sectionId but we have courseId. + promise = $mmCourse.getModuleSectionId(moduleId, courseId, siteId).then(function(id) { + sectionId = id; + }); + } + + return promise.then(function() { + return $state.go('redirect', { + siteid: siteId, + state: 'site.mm_course', + params: { + courseid: courseId, + moduleid: moduleId, + sid: sectionId + } + }); + }); + }).catch(function(error) { + if (error) { + $mmUtil.showErrorModal(error); + } else { + $mmUtil.showErrorModal('mm.course.errorgetmodule', true); + } + return $q.reject(); + }).finally(function() { + modal.dismiss(); + }); + }; + /** * Prefetch or restore the prefetch of one section or all the sections. * If the section is "All sections" it will prefetch all the sections. diff --git a/www/core/components/course/templates/sections.html b/www/core/components/course/templates/sections.html index a8c87b0e941..31567483d92 100644 --- a/www/core/components/course/templates/sections.html +++ b/www/core/components/course/templates/sections.html @@ -1,12 +1,12 @@-
-
+
{{section.name}} diff --git a/www/core/components/courses/services/handlers.js b/www/core/components/courses/services/handlers.js index 466241ac624..e26e7dddf29 100644 --- a/www/core/components/courses/services/handlers.js +++ b/www/core/components/courses/services/handlers.js @@ -151,23 +151,14 @@ angular.module('mm.core.courses') }); } - /** - * Whether or not the handler is enabled for the site. - * - * @return {Boolean} - */ - self.isEnabled = function() { - return true; - }; - /** * Get actions to perform with the link. * - * @param {String} url URL to treat. - * @param {Number} [courseId] Course ID related to the URL. - * @return {Object[]} List of actions. See {@link $mmContentLinksDelegate#registerLinkHandler}. + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @return {Object[]} List of actions. See {@link $mmContentLinksDelegate#registerLinkHandler}. */ - self.getActions = function(url, courseId) { + 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) { @@ -177,6 +168,7 @@ angular.module('mm.core.courses') return [{ message: 'mm.core.view', icon: 'ion-eye', + sites: siteIds, action: function(siteId) { siteId = siteId || $mmSite.getId(); if (siteId == $mmSite.getId()) { diff --git a/www/core/components/user/services/handlers.js b/www/core/components/user/services/handlers.js index fba5ded58cd..b33b859ad12 100644 --- a/www/core/components/user/services/handlers.js +++ b/www/core/components/user/services/handlers.js @@ -36,22 +36,14 @@ angular.module('mm.core.user') var self = {}; - /** - * Whether or not the handler is enabled for the site. - * - * @return {Boolean} - */ - self.isEnabled = function() { - return true; - }; - /** * Get actions to perform with the link. * - * @param {String} url URL to treat. - * @return {Object[]} List of actions. See {@link $mmContentLinksDelegate#registerLinkHandler}. + * @param {String[]} siteIds Site IDs the URL belongs to. + * @param {String} url URL to treat. + * @return {Object[]} List of actions. See {@link $mmContentLinksDelegate#registerLinkHandler}. */ - self.getActions = function(url) { + self.getActions = function(siteIds, url) { // Check it's a user URL. if (url.indexOf('grade/report/user') == -1 && (url.indexOf('/user/view.php') > -1 || url.indexOf('/user/profile.php') > -1)) { @@ -61,6 +53,7 @@ angular.module('mm.core.user') return [{ message: 'mm.core.view', icon: 'ion-eye', + sites: siteIds, action: function(siteId) { var stateParams = { courseid: params.course, diff --git a/www/core/directives/splitview.js b/www/core/directives/splitview.js index 2f11dcae67b..45ba3b457c5 100644 --- a/www/core/directives/splitview.js +++ b/www/core/directives/splitview.js @@ -19,6 +19,12 @@ angular.module('mm.core') /** * Directive to create a split view layout. This directive should be used along with mm-split-view-link. * + * IMPORTANT: Due to a limitation in Angular ui-router, the left pane state and the right pane state should NOT have + * parameters with the same name but different value. It can cause unexpected behaviors. + * Example: if the left pane loads a state with param 'courseid', then all the states that can be loaded in the right pane + * should avoid having a parameter named 'courseid'. The right pane state can have a 'courseid' param only if it will always + * have the same value than in left pane state. + * * @module mm.core * @ngdoc directive * @name mmSplitView @@ -37,6 +43,12 @@ angular.module('mm.core') * immediately load the link set (if no link is set it will load the first link found). Example: * $rootScope.$broadcast(mmCoreSplitViewLoad, {load: 2}); * + * IMPORTANT: Due to a limitation in Angular ui-router, the left pane state and the right pane state should NOT have + * parameters with the same name but different value. It can cause unexpected behaviors. + * Example: if the left pane loads a state with param 'courseid', then all the states that can be loaded in the right pane + * should avoid having a parameter named 'courseid'. The right pane state can have a 'courseid' param only if it will always + * have the same value than in left pane state. + * * Accepts the following params: * * @param {String} [menuWidth] Width of the left menu. Can be specified in pixels ('200px') or in percentage ('30%'). diff --git a/www/core/directives/splitviewlink.js b/www/core/directives/splitviewlink.js index f60b5226ea1..d5ba89f9656 100644 --- a/www/core/directives/splitviewlink.js +++ b/www/core/directives/splitviewlink.js @@ -18,6 +18,12 @@ angular.module('mm.core') * Directive to load a state in a split-view-content pane in tablet or in a new page in phone. * Requires being a child of mmSplitView. * + * IMPORTANT: Due to a limitation in Angular ui-router, the left pane state and the right pane state should NOT have + * parameters with the same name but different value. It can cause unexpected behaviors. + * Example: if the left pane loads a state with param 'courseid', then all the states that can be loaded in the right pane + * should avoid having a parameter named 'courseid'. The right pane state can have a 'courseid' param only if it will always + * have the same value than in left pane state. + * * @module mm.core * @ngdoc directive * @name mmSplitViewLink diff --git a/www/core/main.js b/www/core/main.js index 7bb3ce0dca4..dce34a55f7f 100644 --- a/www/core/main.js +++ b/www/core/main.js @@ -61,6 +61,7 @@ angular.module('mm.core', ['pascalprecht.translate']) state: null, params: null }, + cache: false, controller: function($scope, $state, $stateParams, $mmSite, $mmSitesManager, $ionicHistory) { $ionicHistory.nextViewOptions({disableBack: true});