From 370a24fe47f8d26486fc1b8ce2eea2868109ae4d Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 16 Mar 2016 15:19:44 +0100 Subject: [PATCH 1/2] MOBILE-1465 addons: Hide not available options in user and courses --- .../services/coursecompletion.js | 19 +++++- .../coursecompletion/services/handlers.js | 9 ++- www/addons/grades/services/grades.js | 24 ++++++++ www/addons/grades/services/handlers.js | 4 +- www/addons/notes/services/handlers.js | 11 ++-- www/addons/notes/services/notes.js | 59 +++++++++++++++++-- 6 files changed, 113 insertions(+), 13 deletions(-) diff --git a/www/addons/coursecompletion/services/coursecompletion.js b/www/addons/coursecompletion/services/coursecompletion.js index 2fc75adee5a..61930b5f3d0 100644 --- a/www/addons/coursecompletion/services/coursecompletion.js +++ b/www/addons/coursecompletion/services/coursecompletion.js @@ -106,7 +106,6 @@ angular.module('mm.addons.coursecompletion') preSets = { cacheKey: getCompletionCacheKey(courseid, userid) }; - return $mmSite.read('core_completion_get_course_completion_status', data, preSets).then(function(data) { if (data.completionstatus) { return data.completionstatus; @@ -184,6 +183,24 @@ angular.module('mm.addons.coursecompletion') }); }; + /** + * Returns whether or not the view course completion plugin is enabled for a certain user. + * + * @module mm.addons.coursecompletion + * @ngdoc method + * @name $mmaCourseCompletion#isPluginViewEnabledForCourse + * @param {Number} courseId Course ID. + * @param {Number} userId User ID. + * @return {Promise} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. + */ + self.isPluginViewEnabledForUser = function(courseId, userId) { + return self.getCompletion(courseId, userId).then(function() { + return true; + }).catch(function() { + return false; + }); + }; + /** * Returns whether or not the self completion is available in current site. * diff --git a/www/addons/coursecompletion/services/handlers.js b/www/addons/coursecompletion/services/handlers.js index decb342ec2a..094df99ff04 100644 --- a/www/addons/coursecompletion/services/handlers.js +++ b/www/addons/coursecompletion/services/handlers.js @@ -55,7 +55,9 @@ angular.module('mm.addons.coursecompletion') * @return {Boolean} True if handler is enabled, false otherwise. */ self.isEnabledForUser = function(user, courseId) { - return $mmaCourseCompletion.isPluginViewEnabledForCourse(courseId); + return $mmaCourseCompletion.isPluginViewEnabledForCourse(courseId).then(function() { + return $mmaCourseCompletion.isPluginViewEnabledForUser(courseId, user.id); + }); }; /** @@ -126,7 +128,10 @@ angular.module('mm.addons.coursecompletion') if (accessData && accessData.type == mmCoursesAccessMethods.guest) { return false; // Not enabled for guests. } - return $mmaCourseCompletion.isPluginViewEnabledForCourse(courseId); + return $mmaCourseCompletion.isPluginViewEnabledForCourse(courseId).then(function() { + // Check if the user can see his own report, teachers can't. + return $mmaCourseCompletion.isPluginViewEnabledForUser(courseId); + }); }; /** diff --git a/www/addons/grades/services/grades.js b/www/addons/grades/services/grades.js index 007b0e18a0e..3d203313e13 100644 --- a/www/addons/grades/services/grades.js +++ b/www/addons/grades/services/grades.js @@ -224,6 +224,30 @@ angular.module('mm.addons.grades') }); }; + /** + * Returns whether or not the grade addon is enabled for a certain user. + * + * @module mm.addons.grades + * @ngdoc method + * @name $mmaGrades#isPluginEnabledForUser + * @param {Number} courseId Course ID. + * @param {Number} userId User ID. + * @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.isPluginEnabledForUser = function(courseId, userId) { + // We don't use the getGradesTable function to prevent formatting the table. + var data = { + courseid: courseId, + userid: userId + }; + return $mmSite.read('gradereport_user_get_grades_table', data, {}).then(function() { + return true; + }).catch(function() { + return false; + }); + }; + /** * Get the grades for a certain course. * For now we only support gradereport_user_get_grades_table. It returns the complete grades table. diff --git a/www/addons/grades/services/handlers.js b/www/addons/grades/services/handlers.js index 3ed85f47c91..5797893f279 100644 --- a/www/addons/grades/services/handlers.js +++ b/www/addons/grades/services/handlers.js @@ -118,7 +118,9 @@ angular.module('mm.addons.grades') * @return {Promise} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. */ self.isEnabledForUser = function(user, courseId) { - return $mmaGrades.isPluginEnabledForCourse(courseId); + return $mmaGrades.isPluginEnabledForCourse(courseId).then(function() { + return $mmaGrades.isPluginEnabledForUser(courseId, user.id); + }); }; /** diff --git a/www/addons/notes/services/handlers.js b/www/addons/notes/services/handlers.js index bd7d64abe04..9943037948f 100644 --- a/www/addons/notes/services/handlers.js +++ b/www/addons/notes/services/handlers.js @@ -23,7 +23,7 @@ angular.module('mm.addons.notes') * @ngdoc service * @name $mmaNotesHandlers */ -.factory('$mmaNotesHandlers', function($mmaNotes, $mmSite, $mmApp, $ionicModal, $mmUtil, mmCoursesAccessMethods) { +.factory('$mmaNotesHandlers', function($mmaNotes, $mmSite, $mmApp, $ionicModal, $mmUtil, $q, mmCoursesAccessMethods) { var self = {}; @@ -52,11 +52,14 @@ angular.module('mm.addons.notes') * * @param {Object} user User to check. * @param {Number} courseId Course ID. - * @return {Boolean} True if handler is enabled, false otherwise. + * @return {Promise} Promise resolved with true if enabled, resolved with false otherwise. */ self.isEnabledForUser = function(user, courseId) { // Active course required. - return courseId && user.id != $mmSite.getUserId(); + if (!courseId || user.id == $mmSite.getUserId()) { + return $q.when(false); + } + return $mmaNotes.isPluginAddNoteEnabledForCourse(courseId); }; /** @@ -161,7 +164,7 @@ angular.module('mm.addons.notes') if (accessData && accessData.type == mmCoursesAccessMethods.guest) { return false; // Not enabled for guests. } - return true; + return $mmaNotes.isPluginViewNotesEnabledForCourse(courseId); }; /** diff --git a/www/addons/notes/services/notes.js b/www/addons/notes/services/notes.js index 2fbfae7fd83..b3d486f43a3 100644 --- a/www/addons/notes/services/notes.js +++ b/www/addons/notes/services/notes.js @@ -46,7 +46,16 @@ angular.module('mm.addons.notes') "notes[0][text]": noteText, "notes[0][format]": 1 }; - return $mmSite.write('core_notes_create_notes', data); + return $mmSite.write('core_notes_create_notes', data).then(function(response) { + if (!response || !response.length) { + return $q.reject(); + } + + if (response[0].noteid == -1) { + return $q.reject(response[0].errormessage); + } + return response; + }); }; /** @@ -61,8 +70,6 @@ angular.module('mm.addons.notes') * @return {Boolean} */ self.isPluginAddNoteEnabled = function() { - var infos; - if (!$mmSite.isLoggedIn()) { return false; } else if (!$mmSite.canUseAdvancedFeature('enablenotes')) { @@ -74,6 +81,33 @@ angular.module('mm.addons.notes') return true; }; + /** + * Returns whether or not the add note plugin is enabled for a certain course. + * + * @module mm.addons.notes + * @ngdoc method + * @name $mmaNotes#isPluginAddNoteEnabledForCourse + * @param {Number} courseId ID of the course. + * @return {Promise} Promise resolved with true if enabled, resolved with false otherwise. + */ + self.isPluginAddNoteEnabledForCourse = function(courseId) { + // The only way to detect if it's enabled is to perform a WS call. + // We use an invalid user ID (-1) and check if the error is due to permissions or to invalid ID. + var data = { + "notes[0][userid]" : -1, + "notes[0][courseid]": courseId, + "notes[0][publishstate]": 'personal', + "notes[0][text]": '', + "notes[0][format]": 1 + }; + return $mmSite.read('core_notes_create_notes', data).then(function() { + // User can add notes. + return true; + }).catch(function() { + return false; + }); + }; + /** * Returns whether or not the read notes plugin is enabled for the current site. * @@ -86,8 +120,6 @@ angular.module('mm.addons.notes') * @return {Boolean} */ self.isPluginViewNotesEnabled = function() { - var infos; - if (!$mmSite.isLoggedIn()) { return false; } else if (!$mmSite.canUseAdvancedFeature('enablenotes')) { @@ -99,6 +131,23 @@ angular.module('mm.addons.notes') return true; }; + /** + * Returns whether or not the read notes plugin is enabled for a certain course. + * + * @module mm.addons.notes + * @ngdoc method + * @name $mmaNotes#isPluginViewNotesEnabledForCourse + * @param {Number} courseId ID of the course. + * @return {Promise} Promise resolved with true if enabled, resolved with false otherwise. + */ + self.isPluginViewNotesEnabledForCourse = function(courseId) { + return self.getNotes(courseId).then(function() { + return true; + }).catch(function() { + return false; + }); + }; + /** * Get users notes for a certain site, course and personal notes. * From 1aa3b16744dc411d255363061f2282442eec9448 Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 16 Mar 2016 15:49:34 +0100 Subject: [PATCH 2/2] MOBILE-1465 addons: Use a cache to minimize WS calls in handlers --- .../services/coursecompletion.js | 2 +- .../coursecompletion/services/handlers.js | 74 ++++++++++++++++++- www/addons/grades/services/handlers.js | 48 +++++++++++- www/addons/notes/services/handlers.js | 60 ++++++++++++++- www/addons/notes/services/notes.js | 2 +- .../components/courses/controllers/list.js | 3 +- www/core/components/courses/main.js | 1 + .../components/user/controllers/profile.js | 4 +- www/core/components/user/main.js | 1 + 9 files changed, 183 insertions(+), 12 deletions(-) diff --git a/www/addons/coursecompletion/services/coursecompletion.js b/www/addons/coursecompletion/services/coursecompletion.js index 61930b5f3d0..f2f5d0ae73f 100644 --- a/www/addons/coursecompletion/services/coursecompletion.js +++ b/www/addons/coursecompletion/services/coursecompletion.js @@ -188,7 +188,7 @@ angular.module('mm.addons.coursecompletion') * * @module mm.addons.coursecompletion * @ngdoc method - * @name $mmaCourseCompletion#isPluginViewEnabledForCourse + * @name $mmaCourseCompletion#isPluginViewEnabledForUser * @param {Number} courseId Course ID. * @param {Number} userId User ID. * @return {Promise} Promise resolved with true if plugin is enabled, rejected or resolved with false otherwise. diff --git a/www/addons/coursecompletion/services/handlers.js b/www/addons/coursecompletion/services/handlers.js index 094df99ff04..981be8e2a25 100644 --- a/www/addons/coursecompletion/services/handlers.js +++ b/www/addons/coursecompletion/services/handlers.js @@ -25,7 +25,50 @@ angular.module('mm.addons.coursecompletion') */ .factory('$mmaCourseCompletionHandlers', function($mmaCourseCompletion, $state, mmCoursesAccessMethods) { - var self = {}; + // We use "caches" to decrease network usage. + var self = {}, + viewCompletionEnabledCache = {}, + coursesNavEnabledCache = {}; + + /** + * Get a cache key to identify a course and a user. + * + * @param {Number} courseId Course ID. + * @param {Number} userId User ID. + * @return {String} Cache key. + */ + function getCacheKey(courseId, userId) { + return courseId + '#' + userId; + } + + /** + * Clear view completion cache. + * If a courseId and userId are specified, it will only delete the entry for that user and course. + * + * @module mm.addons.coursecompletion + * @ngdoc method + * @name $mmaCourseCompletionHandlers#clearViewCompletionCache + * @param {Number} [courseId] Course ID. + * @param {Number} [userId] User ID. + */ + self.clearViewCompletionCache = function(courseId, userId) { + if (courseId && userId) { + delete viewCompletionEnabledCache[getCacheKey(courseId, userId)]; + } else { + viewCompletionEnabledCache = {}; + } + }; + + /** + * Clear courses nav caches. + * + * @module mm.addons.coursecompletion + * @ngdoc method + * @name $mmaCourseCompletionHandlers#clearCoursesNavCache + */ + self.clearCoursesNavCache = function() { + coursesNavEnabledCache = {}; + }; /** * View user completion handler. @@ -56,7 +99,14 @@ angular.module('mm.addons.coursecompletion') */ self.isEnabledForUser = function(user, courseId) { return $mmaCourseCompletion.isPluginViewEnabledForCourse(courseId).then(function() { - return $mmaCourseCompletion.isPluginViewEnabledForUser(courseId, user.id); + var cacheKey = getCacheKey(courseId, user.id); + if (typeof viewCompletionEnabledCache[cacheKey] != 'undefined') { + return viewCompletionEnabledCache[cacheKey]; + } + return $mmaCourseCompletion.isPluginViewEnabledForUser(courseId, user.id).then(function(enabled) { + viewCompletionEnabledCache[cacheKey] = enabled; + return enabled; + }); }); }; @@ -130,7 +180,13 @@ angular.module('mm.addons.coursecompletion') } return $mmaCourseCompletion.isPluginViewEnabledForCourse(courseId).then(function() { // Check if the user can see his own report, teachers can't. - return $mmaCourseCompletion.isPluginViewEnabledForUser(courseId); + if (typeof coursesNavEnabledCache[courseId] != 'undefined') { + return coursesNavEnabledCache[courseId]; + } + return $mmaCourseCompletion.isPluginViewEnabledForUser(courseId).then(function(enabled) { + coursesNavEnabledCache[courseId] = enabled; + return enabled; + }); }); }; @@ -166,4 +222,16 @@ angular.module('mm.addons.coursecompletion') }; return self; +}) + +.run(function($mmaCourseCompletionHandlers, $mmEvents, mmCoreEventLogout, mmCoursesEventMyCoursesRefreshed, + mmUserEventProfileRefreshed) { + $mmEvents.on(mmCoreEventLogout, function() { + $mmaCourseCompletionHandlers.clearViewCompletionCache(); + $mmaCourseCompletionHandlers.clearCoursesNavCache(); + }); + $mmEvents.on(mmCoursesEventMyCoursesRefreshed, $mmaCourseCompletionHandlers.clearCoursesNavCache); + $mmEvents.on(mmUserEventProfileRefreshed, function(data) { + $mmaCourseCompletionHandlers.clearViewCompletionCache(data.courseid, data.userid); + }); }); diff --git a/www/addons/grades/services/handlers.js b/www/addons/grades/services/handlers.js index 5797893f279..921cde81ec7 100644 --- a/www/addons/grades/services/handlers.js +++ b/www/addons/grades/services/handlers.js @@ -23,7 +23,37 @@ angular.module('mm.addons.grades') */ .factory('$mmaGradesHandlers', function($mmaGrades, $state, $mmUtil, $mmContentLinksHelper, mmCoursesAccessMethods) { - var self = {}; + var self = {}, + viewGradesEnabledCache = {}; // We use a "cache" to decrease network usage. + + /** + * Get a cache key to identify a course and a user. + * + * @param {Number} courseId Course ID. + * @param {Number} userId User ID. + * @return {String} Cache key. + */ + function getCacheKey(courseId, userId) { + return courseId + '#' + userId; + } + + /** + * Clear view grades cache. + * If a courseId and userId are specified, it will only delete the entry for that user and course. + * + * @module mm.addons.grades + * @ngdoc method + * @name $mmaGradesHandlers#clearViewGradesCache + * @param {Number} [courseId] Course ID. + * @param {Number} [userId] User ID. + */ + self.clearViewGradesCache = function(courseId, userId) { + if (courseId && userId) { + delete viewGradesEnabledCache[getCacheKey(courseId, userId)]; + } else { + viewGradesEnabledCache = {}; + } + }; /** * Course nav handler. @@ -119,7 +149,14 @@ angular.module('mm.addons.grades') */ self.isEnabledForUser = function(user, courseId) { return $mmaGrades.isPluginEnabledForCourse(courseId).then(function() { - return $mmaGrades.isPluginEnabledForUser(courseId, user.id); + var cacheKey = getCacheKey(courseId, user.id); + if (typeof viewGradesEnabledCache[cacheKey] != 'undefined') { + return viewGradesEnabledCache[cacheKey]; + } + return $mmaGrades.isPluginEnabledForUser(courseId, user.id).then(function(enabled) { + viewGradesEnabledCache[cacheKey] = enabled; + return enabled; + }); }); }; @@ -239,4 +276,11 @@ angular.module('mm.addons.grades') }; return self; +}) + +.run(function($mmaGradesHandlers, $mmEvents, mmCoreEventLogout, mmUserEventProfileRefreshed) { + $mmEvents.on(mmCoreEventLogout, $mmaGradesHandlers.clearViewGradesCache); + $mmEvents.on(mmUserEventProfileRefreshed, function(data) { + $mmaGradesHandlers.clearViewGradesCache(data.courseid, data.userid); + }); }); diff --git a/www/addons/notes/services/handlers.js b/www/addons/notes/services/handlers.js index 9943037948f..8e13cfec0bd 100644 --- a/www/addons/notes/services/handlers.js +++ b/www/addons/notes/services/handlers.js @@ -25,7 +25,38 @@ angular.module('mm.addons.notes') */ .factory('$mmaNotesHandlers', function($mmaNotes, $mmSite, $mmApp, $ionicModal, $mmUtil, $q, mmCoursesAccessMethods) { - var self = {}; + // We use "caches" to decrease network usage. + var self = {}, + addNoteEnabledCache = {}, + coursesNavEnabledCache = {}; + + /** + * Clear add note cache. + * If a courseId is specified, it will only delete the entry for that course. + * + * @module mm.addons.notes + * @ngdoc method + * @name $mmaNotesHandlers#clearAddNoteCache + * @param {Number} [courseId] Course ID. + */ + self.clearAddNoteCache = function(courseId) { + if (courseId) { + delete addNoteEnabledCache[courseId]; + } else { + addNoteEnabledCache = {}; + } + }; + + /** + * Clear courses nav cache. + * + * @module mm.addons.notes + * @ngdoc method + * @name $mmaNotesHandlers#clearCoursesNavCache + */ + self.clearCoursesNavCache = function() { + coursesNavEnabledCache = {}; + }; /** * Add a note handler. @@ -59,7 +90,13 @@ angular.module('mm.addons.notes') if (!courseId || user.id == $mmSite.getUserId()) { return $q.when(false); } - return $mmaNotes.isPluginAddNoteEnabledForCourse(courseId); + if (typeof addNoteEnabledCache[courseId] != 'undefined') { + return addNoteEnabledCache[courseId]; + } + return $mmaNotes.isPluginAddNoteEnabledForCourse(courseId).then(function(enabled) { + addNoteEnabledCache[courseId] = enabled; + return enabled; + }); }; /** @@ -164,7 +201,13 @@ angular.module('mm.addons.notes') if (accessData && accessData.type == mmCoursesAccessMethods.guest) { return false; // Not enabled for guests. } - return $mmaNotes.isPluginViewNotesEnabledForCourse(courseId); + if (typeof coursesNavEnabledCache[courseId] != 'undefined') { + return coursesNavEnabledCache[courseId]; + } + return $mmaNotes.isPluginViewNotesEnabledForCourse(courseId).then(function(enabled) { + coursesNavEnabledCache[courseId] = enabled; + return enabled; + }); }; /** @@ -199,4 +242,15 @@ angular.module('mm.addons.notes') }; return self; +}) + +.run(function($mmaNotesHandlers, $mmEvents, mmCoreEventLogout, mmCoursesEventMyCoursesRefreshed, mmUserEventProfileRefreshed) { + $mmEvents.on(mmCoreEventLogout, function() { + $mmaNotesHandlers.clearAddNoteCache(); + $mmaNotesHandlers.clearCoursesNavCache(); + }); + $mmEvents.on(mmCoursesEventMyCoursesRefreshed, $mmaNotesHandlers.clearCoursesNavCache); + $mmEvents.on(mmUserEventProfileRefreshed, function(data) { + $mmaNotesHandlers.clearAddNoteCache(data.courseid); + }); }); diff --git a/www/addons/notes/services/notes.js b/www/addons/notes/services/notes.js index b3d486f43a3..660738b2462 100644 --- a/www/addons/notes/services/notes.js +++ b/www/addons/notes/services/notes.js @@ -92,7 +92,7 @@ angular.module('mm.addons.notes') */ self.isPluginAddNoteEnabledForCourse = function(courseId) { // The only way to detect if it's enabled is to perform a WS call. - // We use an invalid user ID (-1) and check if the error is due to permissions or to invalid ID. + // We use an invalid user ID (-1) to avoid saving the note if the user has permissions. var data = { "notes[0][userid]" : -1, "notes[0][courseid]": courseId, diff --git a/www/core/components/courses/controllers/list.js b/www/core/components/courses/controllers/list.js index 12579d824ff..d7b6958dc06 100644 --- a/www/core/components/courses/controllers/list.js +++ b/www/core/components/courses/controllers/list.js @@ -22,7 +22,7 @@ angular.module('mm.core.courses') * @name mmCoursesListCtrl */ .controller('mmCoursesListCtrl', function($scope, $mmCourses, $mmCoursesDelegate, $mmUtil, $mmEvents, $mmSite, - mmCoursesEventMyCoursesUpdated) { + mmCoursesEventMyCoursesUpdated, mmCoursesEventMyCoursesRefreshed) { $scope.searchEnabled = $mmCourses.isSearchCoursesAvailable(); $scope.areNavHandlersLoadedFor = $mmCoursesDelegate.areNavHandlersLoadedFor; @@ -49,6 +49,7 @@ angular.module('mm.core.courses') }); $scope.refreshCourses = function() { + $mmEvents.trigger(mmCoursesEventMyCoursesRefreshed); $mmCourses.invalidateUserCourses().finally(function() { fetchCourses(true).finally(function() { $scope.$broadcast('scroll.refreshComplete'); diff --git a/www/core/components/courses/main.js b/www/core/components/courses/main.js index 0ef729fa061..b63f67fb573 100644 --- a/www/core/components/courses/main.js +++ b/www/core/components/courses/main.js @@ -18,6 +18,7 @@ angular.module('mm.core.courses', []) .constant('mmCoursesSearchPerPage', 20) // Max of courses per page when searching courses. .constant('mmCoursesEnrolInvalidKey', 'mmCoursesEnrolInvalidKey') .constant('mmCoursesEventMyCoursesUpdated', 'my_courses_updated') +.constant('mmCoursesEventMyCoursesRefreshed', 'my_courses_refreshed') // User refreshed My Courses. .constant('mmCoursesAccessMethods', { guest: 'guest', default: 'default' diff --git a/www/core/components/user/controllers/profile.js b/www/core/components/user/controllers/profile.js index d6469f49733..2977910289c 100644 --- a/www/core/components/user/controllers/profile.js +++ b/www/core/components/user/controllers/profile.js @@ -21,7 +21,8 @@ angular.module('mm.core.user') * @ngdoc controller * @name mmaParticipantsProfileCtrl */ -.controller('mmUserProfileCtrl', function($scope, $stateParams, $mmUtil, $mmUser, $mmUserDelegate, $mmSite, $q, $translate) { +.controller('mmUserProfileCtrl', function($scope, $stateParams, $mmUtil, $mmUser, $mmUserDelegate, $mmSite, $q, $translate, + $mmEvents, mmUserEventProfileRefreshed) { var courseid = $stateParams.courseid, userid = $stateParams.userid; @@ -74,6 +75,7 @@ angular.module('mm.core.user') }); $scope.refreshUser = function() { + $mmEvents.trigger(mmUserEventProfileRefreshed, {courseid: courseid, userid: userid}); $mmUser.invalidateUserCache(userid).finally(function() { fetchUserData().finally(function() { $scope.$broadcast('scroll.refreshComplete'); diff --git a/www/core/components/user/main.js b/www/core/components/user/main.js index f7eb2d31490..111e4cedd4f 100644 --- a/www/core/components/user/main.js +++ b/www/core/components/user/main.js @@ -14,6 +14,7 @@ angular.module('mm.core.user', []) +.constant('mmUserEventProfileRefreshed', 'user_profile_refreshed') // User refreshed an user profile. .value('mmUserProfileState', 'site.mm_user-profile') .config(function($stateProvider, $mmContentLinksDelegateProvider) {