From 46d6f28278b8578801fb7baf7f049932dd3b9723 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 6 Jun 2017 10:42:08 +0200 Subject: [PATCH] MOBILE-2119 data: Show single view entries --- www/addons/mod/data/controllers/entry.js | 172 ++++++++++++++++++ www/addons/mod/data/controllers/index.js | 3 - www/addons/mod/data/main.js | 21 ++- www/addons/mod/data/services/data.js | 83 ++++++--- www/addons/mod/data/services/handlers.js | 61 ++++++- www/addons/mod/data/services/helper.js | 85 ++++++++- .../mod/data/services/prefetch_handler.js | 14 +- www/addons/mod/data/templates/entry.html | 37 ++++ www/addons/mod/data/templates/index.html | 4 +- 9 files changed, 446 insertions(+), 34 deletions(-) create mode 100644 www/addons/mod/data/controllers/entry.js create mode 100644 www/addons/mod/data/templates/entry.html diff --git a/www/addons/mod/data/controllers/entry.js b/www/addons/mod/data/controllers/entry.js new file mode 100644 index 00000000000..644aae9f69d --- /dev/null +++ b/www/addons/mod/data/controllers/entry.js @@ -0,0 +1,172 @@ +// (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_data') + +/** + * Data entry controller. + * + * @module mm.addons.mod_data + * @ngdoc controller + * @name mmaModDataEntryCtrl + */ +.controller('mmaModDataEntryCtrl', function($scope, $stateParams, $mmaModData, mmaModDataComponent, $mmCourse, $q, + $mmText, $translate, $mmUtil, $mmSite, $mmaModDataHelper, $mmGroups, $ionicScrollDelegate) { + + var module = $stateParams.module || {}, + courseId = $stateParams.courseid, + entryId = $stateParams.entryid || false, + page = $stateParams.page || false, + data, + scrollView; + + $scope.title = module.name; + $scope.description = module.description; + $scope.moduleUrl = module.url; + $scope.moduleName = $mmCourse.translateModuleName('data'); + $scope.component = mmaModDataComponent; + $scope.databaseLoaded = false; + $scope.selectedGroup = $stateParams.group || 0; + + function fetchEntryData(refresh) { + return $mmaModData.getDatabase(courseId, module.id).then(function(databaseData) { + data = databaseData; + + $scope.title = data.name || $scope.title; + $scope.description = data.intro || $scope.description; + $scope.data = databaseData; + + $scope.database = data; + + return setEntryIdFromPage(data.id, page, $scope.selectedGroup).then(function() { + return $mmaModData.getDatabaseAccessInformation(data.id); + }); + }).then(function(accessData) { + return $mmGroups.getActivityGroupInfo(data.coursemodule, accessData.canmanageentries).then(function(groupInfo) { + $scope.groupInfo = groupInfo; + + // Check selected group is accessible. + if (groupInfo && groupInfo.groups && groupInfo.groups.length > 0) { + var found = false; + for (var x in groupInfo.groups) { + if (groupInfo.groups[x].id == $scope.selectedGroup) { + found = true; + break; + } + } + if (!found) { + $scope.selectedGroup = groupInfo.groups[0].id; + } + } + + return $mmaModData.getEntry(data.id, entryId); + }); + }).then(function(entry) { + $scope.cssTemplate = $mmaModDataHelper.prefixCSS(data.csstemplate, '.mma-data-entries-' + data.id); + $scope.entryContents = entry.entryviewcontents; + + return $mmaModDataHelper.getPageInfoByEntry(data.id, entryId, $scope.selectedGroup).then(function(result) { + $scope.previousId = result.previousId; + $scope.nextId = result.nextId; + }); + }).catch(function(message) { + if (!refresh) { + // Some call failed, retry without using cache since it might be a new activity. + return refreshAllData(); + } + + $mmUtil.showErrorModalDefault(message, 'mm.course.errorgetmodule', true); + return $q.reject(); + }).finally(function() { + scrollTop(); + $scope.databaseLoaded = true; + }); + } + + // Set group to see the database. + $scope.setGroup = function(groupId) { + $scope.selectedGroup = groupId; + $scope.databaseLoaded = false; + + return setEntryIdFromPage(data.id, 0, $scope.selectedGroup).then(function() { + return fetchEntryData(); + }); + }; + + // Go to selected entry without changing state. + $scope.gotoEntry = function(entry) { + entryId = entry; + page = false; + $scope.databaseLoaded = false; + return fetchEntryData(); + + }; + + // Convenience function to refresh all the data. + function refreshAllData() { + var promises = []; + + promises.push($mmaModData.invalidateDatabaseData(courseId)); + if (data) { + promises.push($mmaModData.invalidateEntryData(data.id, entryId)); + promises.push($mmGroups.invalidateActivityGroupInfo(data.coursemodule)); + promises.push($mmaModData.invalidateEntriesData(data.id)); + } + + return $q.all(promises).finally(function() { + return fetchEntryData(true); + }); + } + + // Convenience function to translate page number to entry identifier. + function setEntryIdFromPage(dataId, pageNumber, group) { + if (pageNumber !== false) { + return $mmaModDataHelper.getPageInfoByPage(dataId, pageNumber, group).then(function(result) { + entryId = result.entryId; + page = false; + }); + } + + return $q.when(); + } + + fetchEntryData(); + + // Scroll to top. + function scrollTop() { + if (!scrollView) { + scrollView = $ionicScrollDelegate.$getByHandle('mmaModDataEntryScroll'); + } + scrollView && scrollView.scrollTop && scrollView.scrollTop(); + } + + // Context Menu Description action. + $scope.expandDescription = function() { + $mmText.expandText($translate.instant('mm.core.description'), $scope.description, false, mmaModDataComponent, module.id); + }; + + // Pull to refresh. + $scope.refreshDatabase = function() { + if ($scope.databaseLoaded) { + return refreshAllData().finally(function() { + $scope.$broadcast('scroll.refreshComplete'); + }); + } + }; + + // Opens search. + $scope.gotoSearch = function() { + $mmUtil.openInApp($mmSite.getURL() + '/mod/data/view.php?mode=asearch&d=' + data.id); + }; +}); diff --git a/www/addons/mod/data/controllers/index.js b/www/addons/mod/data/controllers/index.js index 442ca61b38b..35f52d9c5fd 100644 --- a/www/addons/mod/data/controllers/index.js +++ b/www/addons/mod/data/controllers/index.js @@ -36,7 +36,6 @@ angular.module('mm.addons.mod_data') $scope.refreshIcon = 'spinner'; $scope.syncIcon = 'spinner'; $scope.component = mmaModDataComponent; - $scope.componentId = module.id; $scope.databaseLoaded = false; $scope.selectedGroup = $stateParams.group || 0; @@ -135,8 +134,6 @@ angular.module('mm.addons.mod_data') }); }; - - // Convenience function to refresh all the data. function refreshAllData(sync, showErrors) { var promises = []; diff --git a/www/addons/mod/data/main.js b/www/addons/mod/data/main.js index 38a967c9627..01408a1af9a 100644 --- a/www/addons/mod/data/main.js +++ b/www/addons/mod/data/main.js @@ -33,6 +33,24 @@ angular.module('mm.addons.mod_data', ['mm.core']) templateUrl: 'addons/mod/data/templates/index.html' } } + }) + + .state('site.mod_data-entry', { + url: '/mod_data-entry', + params: { + module: null, + moduleid: null, // Redundant parameter to fix a problem passing object as parameters. To be fixed in MOBILE-1370. + courseid: null, + entryid: null, + page: null, + group: null + }, + views: { + 'site': { + controller: 'mmaModDataEntryCtrl', + templateUrl: 'addons/mod/data/templates/entry.html' + } + } }); }) @@ -41,4 +59,5 @@ angular.module('mm.addons.mod_data', ['mm.core']) $mmCourseDelegateProvider.registerContentHandler('mmaModData', 'data', '$mmaModDataHandlers.courseContent'); $mmCoursePrefetchDelegateProvider.registerPrefetchHandler('mmaModData', 'data', '$mmaModDataPrefetchHandler'); $mmContentLinksDelegateProvider.registerLinkHandler('mmaModData:index', '$mmaModDataHandlers.indexLinksHandler'); -}); \ No newline at end of file + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModData:entry', '$mmaModDataHandlers.showEntryLinksHandler'); +}); diff --git a/www/addons/mod/data/services/data.js b/www/addons/mod/data/services/data.js index beb2df25881..7c27dd20779 100644 --- a/www/addons/mod/data/services/data.js +++ b/www/addons/mod/data/services/data.js @@ -89,6 +89,17 @@ angular.module('mm.addons.mod_data') return getEntriesPrefixCacheKey(dataId) + groupId; } + /** + * Get cache key for database entry data WS calls. + * + * @param {Number} dataId Data ID for caching purposes. + * @param {Number} entryId Entry ID. + * @return {String} Cache key. + */ + function getEntryCacheKey(dataId, entryId) { + return getDatabaseDataPrefixCacheKey(dataId) + ':entry:' + entryId; + } + /** * Get cache key for database fields data WS calls. * @@ -297,7 +308,10 @@ angular.module('mm.addons.mod_data') return $mmSitesManager.getSite(siteId).then(function(site) { var params = { databaseid: dataId, - returncontents: 1 + returncontents: 1, + page: page || 0, + perpage: perPage || 0, + groupid: groupId || 0 }, preSets = { cacheKey: getEntriesCacheKey(dataId, groupId) @@ -307,22 +321,10 @@ angular.module('mm.addons.mod_data') params.sort = sort; } - if (order) { + if (typeof order !== "undefined") { params.order = order; } - if (page) { - params.page = page; - } - - if (perPage) { - params.perpage = perPage; - } - - if (typeof groupId !== "undefined") { - params.groupid = groupId; - } - if (forceCache) { preSets.omitExpires = true; } else if (ignoreCache) { @@ -350,6 +352,48 @@ angular.module('mm.addons.mod_data') }); }; + /** + * Get an entry of the database activity. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#getEntry + * @param {Number} dataId Data ID for caching purposes. + * @param {Number} entryId Entry ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the database entry is retrieved. + */ + self.getEntry = function(dataId, entryId, siteId) { + return $mmSitesManager.getSite(siteId).then(function(site) { + var params = { + entryid: entryId, + returncontents: 1 + }, + preSets = { + cacheKey: getEntryCacheKey(dataId, entryId) + }; + + return site.read('mod_data_get_entry', params, preSets); + }); + }; + + /** + * Invalidates database entry data. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#invalidateEntryData + * @param {Number} dataId Data ID for caching purposes. + * @param {Number} entryId Entry ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + self.invalidateEntryData = function(dataId, entryId, siteId) { + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.invalidateWsCacheForKey(getEntryCacheKey(dataId, entryId)); + }); + }; + /** * Get the list of configured fields for the given database. * @@ -412,11 +456,6 @@ angular.module('mm.addons.mod_data') */ self.fetchAllEntries = function(dataId, groupId, sort, order, perPage, forceCache, ignoreCache, siteId) { siteId = siteId || $mmSite.getId(); - - if (typeof perPage == 'undefined') { - perPage = 10; - } - return fetchEntriesRecursive(dataId, groupId, sort, order, perPage, forceCache, ignoreCache, [], 0, siteId); }; @@ -440,9 +479,9 @@ angular.module('mm.addons.mod_data') entries = entries.concat(result.entries); var canLoadMore = ((page + 1) * perPage) < result.totalcount; - if (canLoadMore) { - return fetchEntriesRecursive(dataId, groupId, sort, order, perPage, forceCache, ignoreCache, entries, - page + 1, siteId); + if (perPage && canLoadMore) { + return fetchEntriesRecursive(dataId, groupId, sort, order, perPage, forceCache, ignoreCache, entries, page + 1, + siteId); } return entries; }); diff --git a/www/addons/mod/data/services/handlers.js b/www/addons/mod/data/services/handlers.js index 4220d647011..19b727ce4be 100644 --- a/www/addons/mod/data/services/handlers.js +++ b/www/addons/mod/data/services/handlers.js @@ -22,7 +22,7 @@ angular.module('mm.addons.mod_data') * @name $mmaModDataHandlers */ .factory('$mmaModDataHandlers', function($mmCourse, $mmaModData, $state, $mmContentLinksHelper, $mmUtil, $mmEvents, $mmSite, - mmaModDataComponent, $mmaModDataPrefetchHandler, mmCoreDownloading, mmCoreNotDownloaded, + mmaModDataComponent, $mmaModDataPrefetchHandler, mmCoreDownloading, mmCoreNotDownloaded, $mmContentLinkHandlerFactory, mmCoreEventPackageStatusChanged, mmCoreOutdated, $mmCoursePrefetchDelegate) { var self = {}; @@ -156,5 +156,64 @@ angular.module('mm.addons.mod_data') self.indexLinksHandler = $mmContentLinksHelper.createModuleIndexLinkHandler('mmaModData', 'data', $mmaModData); + /** + * Content links handler for database show entry. + * Match mod/data/view.php?d=6&rid=5 with a valid data id and entryid. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataHandlers#showEntryLinksHandler + */ + self.showEntryLinksHandler = $mmContentLinkHandlerFactory.createChild( + /\/mod\/data\/view\.php.*([\?\&](d|rid|page|group|mode)=\d+)/, '$mmCourseDelegate_mmaModData'); + + // Check if the printLinksHandler is enabled for a certain site. See $mmContentLinkHandlerFactory#isEnabled. + self.showEntryLinksHandler.isEnabled = $mmaModData.isPluginEnabled; + + // Get actions to perform with the link. See $mmContentLinkHandlerFactory#getActions. + self.showEntryLinksHandler.getActions = function(siteIds, url, params, courseId) { + if (typeof params.d == 'undefined') { + // Id not defined. Cannot treat the URL. + return false; + } + + if ((!params.mode || params.mode != "single") && typeof params.rid == 'undefined') { + return false; + } + + return [{ + action: function(siteId) { + var modal = $mmUtil.showModalLoading(), + dataId = parseInt(params.d, 10), + rId = parseInt(params.rid, 10) || false, + group = parseInt(params.group, 10) || false, + page = parseInt(params.page, 10) || false; + + return $mmCourse.getModuleBasicInfoByInstance(dataId, 'data', siteId).then(function(module) { + var stateParams = { + moduleid: module.id, + module: module, + courseid: module.course + }; + + if (group) { + stateParams.group = group; + } + + if (params.mode && params.mode == "single") { + stateParams.page = page || 1; + } else if (rId) { + stateParams.entryid = rId; + } + + + return $mmContentLinksHelper.goInSite('site.mod_data-entry', stateParams, siteId); + }).finally(function() { + modal.dismiss(); + }); + } + }]; + }; + return self; }); diff --git a/www/addons/mod/data/services/helper.js b/www/addons/mod/data/services/helper.js index d2647d6c648..d625a691374 100644 --- a/www/addons/mod/data/services/helper.js +++ b/www/addons/mod/data/services/helper.js @@ -21,7 +21,7 @@ angular.module('mm.addons.mod_data') * @ngdoc service * @name $mmaModDataHelper */ -.factory('$mmaModDataHelper', function() { +.factory('$mmaModDataHelper', function($mmaModData) { var self = {}; @@ -47,5 +47,86 @@ angular.module('mm.addons.mod_data') return css.replace(regExp, prefix + " $1 $2"); }; + + /** + * Get page info related to an entry. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataHelper#getPageInfoByEntry + * @param {Number} dataId Data ID. + * @param {Number} entryId Entry ID. + * @param {Number} groupId Group ID. + * @param {String} siteId Site ID. Current if not defined. + * @return {Promise} Containing page number, if has next and have following page. + */ + self.getPageInfoByEntry = function(dataId, entryId, groupId, siteId) { + return self.getAllEntriesIds(dataId, groupId, siteId).then(function(entries) { + for (var index in entries) { + if (entries[index] == entryId) { + index = parseInt(index, 10); + return { + previousId: entries[index - 1] || false, + nextId: entries[index + 1] || false, + entryId: entryId, + page: index + 1, // Parsed to natural language. + numEntries: entries.length + }; + } + } + return false; + }); + }; + + /** + * Get page info related to an entry by page number. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataHelper#getPageInfoByPage + * @param {Number} dataId Data ID. + * @param {Number} page Page number. + * @param {Number} groupId Group ID. + * @param {String} siteId Site ID. Current if not defined. + * @return {Promise} Containing page number, if has next and have following page. + */ + self.getPageInfoByPage = function(dataId, page, groupId, siteId) { + return self.getAllEntriesIds(dataId, groupId, siteId).then(function(entries) { + var index = parseInt(page, 10) - 1, + entryId = entries[index]; + if (entryId) { + return { + previousId: entries[index - 1] || false, + nextId: entries[index + 1] || false, + entryId: entryId, + page: page, // Parsed to natural language. + numEntries: entries.length + }; + } + return false; + }); + }; + + /** + * Fetch all entries and return it's Id + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataHelper#getAllEntriesIds + * @param {Number} dataId Data ID. + * @param {Number} groupId Group ID. + * @param {String} siteId Site ID. Current if not defined. + * @return {Promise} Containing and array of EntryId. + */ + self.getAllEntriesIds = function(dataId, groupId, siteId) { + return $mmaModData.fetchAllEntries(dataId, groupId, undefined, undefined, undefined, true, undefined, siteId) + .then(function(entries) { + return entries.map(function(entry) { + return entry.id; + }); + }); + }; + + return self; -}); \ No newline at end of file +}); diff --git a/www/addons/mod/data/services/prefetch_handler.js b/www/addons/mod/data/services/prefetch_handler.js index c9b1e05bab1..7f22770fca7 100644 --- a/www/addons/mod/data/services/prefetch_handler.js +++ b/www/addons/mod/data/services/prefetch_handler.js @@ -22,7 +22,7 @@ angular.module('mm.addons.mod_data') * @name $mmaModDataPrefetchHandler */ .factory('$mmaModDataPrefetchHandler', function($mmaModData, mmaModDataComponent, $mmFilepool, $q, $mmUtil, $mmPrefetchFactory, - $mmSite, $mmGroups) { + $mmSite, $mmGroups, $mmCourse) { var self = $mmPrefetchFactory.createPrefetchHandler(mmaModDataComponent); @@ -138,7 +138,7 @@ angular.module('mm.addons.mod_data') var uniqueEntries = {}; angular.forEach(responses, function(groupEntries) { - angular.forEach(groupEntries.entries, function(entry) { + angular.forEach(groupEntries, function(entry) { uniqueEntries[entry.id] = entry; }); }); @@ -326,8 +326,16 @@ angular.module('mm.addons.mod_data') promises.push($mmFilepool.addFilesToQueueByUrl(siteId, info.files, self.component, module.id)); angular.forEach(info.groups, function(group) { - promises.push($mmaModData.getDatabaseAccessInformation(database.id, group.id, false, true, siteId)); + promises.push($mmaModData.getDatabaseAccessInformation(database.id, group.id, false, true, siteId)); }); + + angular.forEach(info.entries, function(entry) { + promises.push($mmaModData.getEntry(database.id, entry.id, siteId)); + }); + + // Add Basic Info to manage links. + promises.push($mmCourse.getModuleBasicInfoByInstance(database.id, 'data', siteId)); + return $q.all(promises); }).then(function() { // Get revision and timemodified. diff --git a/www/addons/mod/data/templates/entry.html b/www/addons/mod/data/templates/entry.html new file mode 100644 index 00000000000..759db3c2cc0 --- /dev/null +++ b/www/addons/mod/data/templates/entry.html @@ -0,0 +1,37 @@ + + {{ title }} + + + + + + + + + +
+ {{ 'mm.core.groupsseparate' | translate }} + {{ 'mm.core.groupsvisible' | translate }} + +
+
+ + + + {{ entryContents }} + +
+
+ + +
+
+
+
diff --git a/www/addons/mod/data/templates/index.html b/www/addons/mod/data/templates/index.html index 3e4cf439b8a..1c9b7e7623e 100644 --- a/www/addons/mod/data/templates/index.html +++ b/www/addons/mod/data/templates/index.html @@ -17,7 +17,7 @@
- {{ description }} + {{ description }}
@@ -48,7 +48,7 @@ {{ cssTemplate }} - + {{ entries }}