diff --git a/www/addons/mod/data/controllers/index.js b/www/addons/mod/data/controllers/index.js new file mode 100644 index 00000000000..cd3840b7ce2 --- /dev/null +++ b/www/addons/mod/data/controllers/index.js @@ -0,0 +1,155 @@ +// (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 index controller. + * + * @module mm.addons.mod_data + * @ngdoc controller + * @name mmaModDataIndexCtrl + */ +.controller('mmaModDataIndexCtrl', function($scope, $stateParams, $mmaModData, mmaModDataComponent, $mmCourse, $mmCourseHelper, $q, + $mmText, $translate, $mmEvents, mmCoreEventOnlineStatusChanged, $mmApp, $mmUtil, $mmSite) { + + var module = $stateParams.module || {}, + courseId = $stateParams.courseid, + data, + onlineObserver; + + $scope.title = module.name; + $scope.description = module.description; + $scope.moduleUrl = module.url; + $scope.moduleName = $mmCourse.translateModuleName('data'); + $scope.courseId = courseId; + $scope.refreshIcon = 'spinner'; + $scope.syncIcon = 'spinner'; + $scope.component = mmaModDataComponent; + $scope.componentId = module.id; + $scope.databaseLoaded = false; + + + function fetchDatabaseData(refresh, sync, showErrors) { + $scope.isOnline = $mmApp.isOnline(); + + return $mmaModData.getDatabase(courseId, module.id).then(function(databaseData) { + data = databaseData; + + $scope.title = data.name || $scope.title; + $scope.description = data.intro || $scope.description; + + $scope.database = data; + + return $mmaModData.getDatabaseAccessInformation(data.id); + }).then(function(accessData) { + $scope.access = accessData; + + if (!accessData.timeavailable) { + var time = $mmUtil.timestamp(); + + $scope.timeAvailableFrom = data.timeavailablefrom && time < data.timeavailablefrom ? + parseInt(data.timeavailablefrom, 10) * 1000 : false; + $scope.timeAvailableFromReadable = $scope.timeAvailableFrom ? + moment($scope.timeAvailableFrom).format('LLL') : false; + $scope.timeAvailableTo = data.timeavailableto && time > data.timeavailableto ? + parseInt(data.timeavailableto, 10) * 1000 : false; + $scope.timeAvailableToReadable = $scope.timeAvailableTo ? moment($scope.timeAvailableTo).format('LLL') : false; + } else { + // TODO: Calculate num entries based on get_entries WS. + $scope.numEntries = accessData.numentries; + $scope.entriesLeftToView = accessData.entrieslefttoview; + $scope.entriesLeftToAdd = accessData.entrieslefttoadd; + $scope.canAdd = accessData.canaddentry; + } + }).then(function() { + // All data obtained, now fill the context menu. + $mmCourseHelper.fillContextMenu($scope, module, courseId, refresh, mmaModDataComponent); + }).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(){ + $scope.databaseLoaded = true; + }); + } + + // Convenience function to refresh all the data. + function refreshAllData(sync, showErrors) { + var promises = []; + + return $q.all(promises).finally(function() { + return fetchDatabaseData(true, sync, showErrors); + }); + } + + fetchDatabaseData(false, true).then(function() { + $mmaModData.logView(data.id).then(function() { + $mmCourse.checkModuleCompletion(courseId, module.completionstatus); + }); + }).finally(function() { + $scope.refreshIcon = 'ion-refresh'; + $scope.syncIcon = 'ion-loop'; + }); + + // Confirm and Remove action. + $scope.removeFiles = function() { + $mmCourseHelper.confirmAndRemove(module, courseId); + }; + + // Context Menu Prefetch action. + $scope.prefetch = function() { + $mmCourseHelper.contextMenuPrefetch($scope, module, courseId); + }; + // 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(showErrors) { + if ($scope.databaseLoaded) { + $scope.refreshIcon = 'spinner'; + $scope.syncIcon = 'spinner'; + return refreshAllData(true, showErrors).finally(function() { + $scope.refreshIcon = 'ion-refresh'; + $scope.syncIcon = 'ion-loop'; + $scope.$broadcast('scroll.refreshComplete'); + }); + } + }; + + // Opens search. + $scope.gotoSearch = function() { + $mmUtil.openInApp($mmSite.getURL() + '/mod/data/view.php?mode=asearch&d=' + data.id); + }; + + // Opens add entries form + $scope.gotoAddEntries = function() { + $mmUtil.openInApp($mmSite.getURL() + '/mod/data/edit.php?d=' + data.id); + }; + + // Refresh online status when changes. + onlineObserver = $mmEvents.on(mmCoreEventOnlineStatusChanged, function(online) { + $scope.isOnline = online; + }); + + $scope.$on('$destroy', function() { + onlineObserver && onlineObserver.off && onlineObserver.off(); + }); +}); diff --git a/www/addons/mod/data/lang/en.json b/www/addons/mod/data/lang/en.json new file mode 100644 index 00000000000..5fc410aa1c6 --- /dev/null +++ b/www/addons/mod/data/lang/en.json @@ -0,0 +1,9 @@ +{ + "addentries": "Add entries", + "entrieslefttoadd": "You must add {{$a.entriesleft}} more entry/entries in order to complete this activity", + "entrieslefttoaddtoview": "You must add {{$a.entrieslefttoview}} more entry/entries before you can view other participants' entries.", + "expired": "Sorry, this activity closed on {{$a}} and is no longer available", + "norecords": "No entries in database", + "notopenyet": "Sorry, this activity is not available until {{$a}}", + "search": "Search" +} diff --git a/www/addons/mod/data/main.js b/www/addons/mod/data/main.js new file mode 100644 index 00000000000..eee37ce60f0 --- /dev/null +++ b/www/addons/mod/data/main.js @@ -0,0 +1,42 @@ +// (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', ['mm.core']) + +.constant('mmaModDataComponent', 'mmaModData') + +.config(function($stateProvider) { + + $stateProvider + + .state('site.mod_data', { + url: '/mod_data', + params: { + module: null, + courseid: null + }, + views: { + 'site': { + controller: 'mmaModDataIndexCtrl', + templateUrl: 'addons/mod/data/templates/index.html' + } + } + }); + +}) + +.config(function($mmCourseDelegateProvider, $mmContentLinksDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModData', 'data', '$mmaModDataHandlers.courseContent'); + $mmContentLinksDelegateProvider.registerLinkHandler('mmaModData', '$mmaModDataHandlers.linksHandler'); +}); \ No newline at end of file diff --git a/www/addons/mod/data/services/data.js b/www/addons/mod/data/services/data.js new file mode 100644 index 00000000000..4e06dbb4eff --- /dev/null +++ b/www/addons/mod/data/services/data.js @@ -0,0 +1,280 @@ +// (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 service. + * + * @module mm.addons.mod_data + * @ngdoc controller + * @name $mmaModData + */ +.factory('$mmaModData', function($q, $mmSitesManager, mmaModDataComponent, $mmFilepool) { + var self = {}; + + /** + * Get cache key for database data WS calls. + * + * @param {Number} courseId Course ID. + * @return {String} Cache key. + */ + function getDatabaseDataCacheKey(courseId) { + return 'mmaModData:data:' + courseId; + } + + /** + * Get prefix cache key for all database activity data WS calls. + * + * @param {Number} dataId Data ID. + * @return {String} Cache key. + */ + function getDatabaseDataPrefixCacheKey(dataId) { + return 'mmaModData:' + dataId; + } + + /** + * Get cache key for database access information data WS calls. + * + * @param {Number} dataId Data ID. + * @return {String} Cache key. + */ + function getDatabaseAccessInformationDataCacheKey(dataId) { + return getDatabaseDataPrefixCacheKey(dataId) + ':access'; + } + + /** + * Return whether or not the plugin is enabled in a certain site. Plugin is enabled if the database WS are available. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#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) { + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.wsAvailable('mod_data_get_databases_by_courses') && + site.wsAvailable('mod_data_get_data_access_information'); + }); + }; + + /** + * Get a database with key=value. If more than one is found, only the first will be returned. + * + * @param {Number} courseId Course ID. + * @param {String} key Name of the property to check. + * @param {Mixed} value Value to search. + * @param {String} [siteId] Site ID. If not defined, current site. + * @param {Boolean} [forceCache] True to always get the value from cache, false otherwise. Default false. + * @return {Promise} Promise resolved when the database is retrieved. + */ + function getDatabase(courseId, key, value, siteId, forceCache) { + return $mmSitesManager.getSite(siteId).then(function(site) { + var params = { + courseids: [courseId] + }, + preSets = { + cacheKey: getDatabaseDataCacheKey(courseId) + }; + + if (forceCache) { + preSets.omitExpires = true; + } + + return site.read('mod_data_get_databases_by_courses', params, preSets).then(function(response) { + if (response && response.databases) { + for (var x in response.databases) { + if (response.databases[x][key] == value) { + return response.databases[x]; + } + } + } + return $q.reject(); + }); + }); + } + + /** + * Get a database by course module ID. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#getDatabase + * @param {Number} courseId Course ID. + * @param {Number} cmId Course module ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @param {Boolean} [forceCache] True to always get the value from cache, false otherwise. Default false. + * @return {Promise} Promise resolved when the database is retrieved. + */ + self.getDatabase = function(courseId, cmId, siteId, forceCache) { + return getDatabase(courseId, 'coursemodule', cmId, siteId, forceCache); + }; + + /** + * Get a database by ID. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#getDatabaseById + * @param {Number} courseId Course ID. + * @param {Number} id Data ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @param {Boolean} [forceCache] True to always get the value from cache, false otherwise. Default false. + * @return {Promise} Promise resolved when the database is retrieved. + */ + self.getDatabaseById = function(courseId, id, siteId, forceCache) { + return getDatabase(courseId, 'id', id, siteId, forceCache); + }; + + /** + * Invalidates database data. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#invalidateDatabaseData + * @param {Number} courseId Course ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the database is invalidated. + */ + self.invalidateDatabaseData = function(courseId, siteId) { + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.invalidateWsCacheForKey(getDatabaseDataCacheKey(courseId)); + }); + }; + + /** + * Invalidates database data except files and module info. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#invalidateDatabaseWSData + * @param {Number} databaseId Data ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + self.invalidateDatabaseWSData = function(databaseId, siteId) { + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.invalidateWsCacheForKeyStartingWith(getDatabaseDataPrefixCacheKey(databaseId)); + + }); + }; + + /** + * Get access information for a given database. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#getDatabaseAccessInformation + * @param {Number} dataId Data ID. + * @param {Boolean} offline True if it should return cached data. Has priority over ignoreCache. + * @param {Boolean} ignoreCache True if it should ignore cached data (it will always fail in offline or server down). + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the database is retrieved. + */ + self.getDatabaseAccessInformation = function(dataId, offline, ignoreCache, siteId) { + return $mmSitesManager.getSite(siteId).then(function(site) { + var params = { + databaseid: dataId + }, + preSets = { + cacheKey: getDatabaseAccessInformationDataCacheKey(dataId) + }; + + if (offline) { + preSets.omitExpires = true; + } else if (ignoreCache) { + preSets.getFromCache = 0; + preSets.emergencyCache = 0; + } + + return site.read('mod_data_get_data_access_information', params, preSets); + }); + }; + + /** + * Invalidates database access information data. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#invalidateDatabaseAccessInformationData + * @param {Number} dataId Data ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + self.invalidateDatabaseAccessInformationData = function(dataId, siteId) { + return $mmSitesManager.getSite(siteId).then(function(site) { + return site.invalidateWsCacheForKey(getDatabaseAccessInformationDataCacheKey(dataId)); + }); + }; + + /** + * Invalidate the prefetched content except files. + * To invalidate files, use $mmaModData#invalidateFiles. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#invalidateContent + * @param {Number} moduleId The module ID. + * @param {Number} courseId Course ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} + */ + self.invalidateContent = function(moduleId, courseId, siteId) { + siteId = siteId || $mmSite.getId(); + return self.getDatabase(courseId, moduleId, siteId, true).then(function(data) { + var ps = []; + // Do not invalidate database data before getting database info, we need it! + ps.push(self.invalidateDatabaseData(courseId, siteId)); + ps.push(self.invalidateDatabaseWSData(data.id, siteId)); + + return $q.all(ps); + }); + }; + + /** + * Invalidate the prefetched files. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#invalidateFiles + * @param {Number} moduleId The module ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the files are invalidated. + */ + self.invalidateFiles = function(moduleId, siteId) { + return $mmFilepool.invalidateFilesByComponent(siteId, mmaModDataComponent, moduleId); + }; + + /** + * Report the database as being viewed. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModData#logView + * @param {String} id Data ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the WS call is successful. + */ + self.logView = function(id, siteId) { + return $mmSitesManager.getSite(siteId).then(function(site) { + var params = { + databaseid: id + }; + return site.write('mod_data_view_database', params); + }); + }; + + return self; +}); diff --git a/www/addons/mod/data/services/handlers.js b/www/addons/mod/data/services/handlers.js new file mode 100644 index 00000000000..4220d647011 --- /dev/null +++ b/www/addons/mod/data/services/handlers.js @@ -0,0 +1,160 @@ +// (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') + +/** + * Mod data handlers. + * + * @module mm.addons.mod_data + * @ngdoc service + * @name $mmaModDataHandlers + */ +.factory('$mmaModDataHandlers', function($mmCourse, $mmaModData, $state, $mmContentLinksHelper, $mmUtil, $mmEvents, $mmSite, + mmaModDataComponent, $mmaModDataPrefetchHandler, mmCoreDownloading, mmCoreNotDownloaded, + mmCoreEventPackageStatusChanged, mmCoreOutdated, $mmCoursePrefetchDelegate) { + var self = {}; + + /** + * Course content handler. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataHandlers#courseContent + */ + self.courseContent = function() { + + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @return {Boolean} + */ + self.isEnabled = function() { + return $mmaModData.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 = { + hidden: true, + icon: 'ion-ios-cloud-download-outline', + label: 'mm.core.download', + action: function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + download(); + } + }, + refreshBtn = { + hidden: true, + icon: 'ion-android-refresh', + label: 'mm.core.refresh', + action: function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $mmaModData.invalidateContent(module.id, courseId).finally(function() { + download(); + }); + } + }; + + $scope.title = module.name; + $scope.icon = $mmCourse.getModuleIconSrc('data'); + $scope.class = 'mma-mod_data-handler'; + $scope.buttons = [downloadBtn, refreshBtn]; + $scope.spinner = true; // Show spinner while calculating status. + + $scope.action = function(e) { + if (e) { + e.preventDefault(); + e.stopPropagation(); + } + $state.go('site.mod_data', {module: module, moduleid: module.id, courseid: courseId}); + }; + + function download() { + + $scope.spinner = true; // Show spinner since this operation might take a while. + // We need to call getDownloadSize, the package might have been updated. + $mmaModDataPrefetchHandler.getDownloadSize(module, courseId).then(function(size) { + $mmUtil.confirmDownloadSize(size).then(function() { + $mmaModDataPrefetchHandler.prefetch(module, courseId).catch(function(error) { + if (!$scope.$$destroyed) { + $mmUtil.showErrorModalDefault(error, 'mm.core.errordownloading', true); + } + }); + }).catch(function() { + // User hasn't confirmed, stop spinner. + $scope.spinner = false; + }); + }).catch(function(error) { + $scope.spinner = false; + $mmUtil.showErrorModalDefault(error, 'mm.core.errordownloading', true); + }); + } + + // 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 === mmaModDataComponent) { + showStatus(data.status); + } + }); + + // Get current status to decide which icon should be shown. + $mmCoursePrefetchDelegate.getModuleStatus(module, courseId).then(showStatus); + + $scope.$on('$destroy', function() { + statusObserver && statusObserver.off && statusObserver.off(); + }); + }; + }; + + return self; + }; + + /** + * Content links handler for module index page. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataHandlers#indexLinksHandler + */ + self.indexLinksHandler = $mmContentLinksHelper.createModuleIndexLinkHandler('mmaModData', 'data', $mmaModData); + + + return self; +}); diff --git a/www/addons/mod/data/services/prefetch_handler.js b/www/addons/mod/data/services/prefetch_handler.js new file mode 100644 index 00000000000..9e3d181ea6c --- /dev/null +++ b/www/addons/mod/data/services/prefetch_handler.js @@ -0,0 +1,249 @@ +// (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') + +/** + * Mod data prefetch handler. + * + * @module mm.addons.mod_data + * @ngdoc service + * @name $mmaModDataPrefetchHandler + */ +.factory('$mmaModDataPrefetchHandler', function($mmaModData, mmaModDataComponent, $mmFilepool, $q, $mmUtil, $mmPrefetchFactory) { + + var self = $mmPrefetchFactory.createPrefetchHandler(mmaModDataComponent); + + // RegExp to check if a module has updates based on the result of $mmCoursePrefetchDelegate#getCourseUpdates. + self.updatesNames = /^configuration$|^.*files$|^entries|^gradeitems$|^outcomes$|^comments$|^ratings/; + + /** + * Download the module. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#download + * @param {Object} module The module object returned by WS. + * @param {Number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved when all files have been downloaded. Data returned is not reliable. + */ + self.download = function(module, courseId) { + // Database cannot be downloaded right away, only prefetched. + return self.prefetch(module, courseId); + }; + + /** + * Get the list of downloadable files. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#getFiles + * @param {Object} module Module to get the files. + * @param {Number} courseId Course ID the module belongs to. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with the list of files. + */ + self.getFiles = function(module, courseId, siteId) { + var files = []; + return $mmaModData.getDatabase(courseId, module.id, siteId).then(function(database) { + // Get intro files. + files = self.getIntroFilesFromInstance(module, database); + return files; + }).catch(function() { + // Any error, return the list we have. + return files; + }); + }; + + /** + * Returns database intro files. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#getIntroFiles + * @param {Object} module The module object returned by WS. + * @param {Number} courseId Course ID. + * @return {Promise} Promise resolved with list of intro files. + */ + self.getIntroFiles = function(module, courseId) { + return $mmaModData.getDatabase(courseId, module.id).catch(function() { + // Not found, return undefined so module description is used. + }).then(function(database) { + return self.getIntroFilesFromInstance(module, database); + }); + }; + + /** + * Get revision of a data. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#getRevision + * @param {Object} module Module to get the revision. + * @param {Number} courseId Course ID the module belongs to. + * @return {Number} Promise resolved with revision. + */ + self.getRevision = function(module, courseId) { + // Data will always be controlled using the getCourseUpdates. + return 0; + }; + + /** + * Get timemodified of a data. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#getTimemodified + * @param {Object} module Module to get the timemodified. + * @param {Number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved with timemodified. + */ + self.getTimemodified = function(module, courseId) { + var siteId = $mmSite.getId(); + + return $mmaModData.getDatabase(courseId, module.id, siteId).then(function(database) { + var files = self.getIntroFilesFromInstance(module, database); + + return Math.max(database.timemodified || 0, $mmFilepool.getTimemodifiedFromFileList(files)); + }).catch(function() { + return 0; + }); + }; + + /** + * Invalidate the prefetched content. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#invalidateContent + * @param {Number} moduleId The module ID. + * @param {Number} courseId Course ID of the module. + * @return {Promise} + */ + self.invalidateContent = function(moduleId, courseId) { + return $mmaModData.invalidateContent(moduleId, courseId); + }; + + /** + * Invalidates WS calls needed to determine module status. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#invalidateModule + * @param {Object} module Module to invalidate. + * @param {Number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved when done. + */ + self.invalidateModule = function(module, courseId) { + return $mmaModData.invalidateDatabaseData(courseId); + }; + + /** + * Check if a database is downloadable. + * A database isn't downloadable if it's not open yet. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#isDownloadable + * @param {Object} module Module to check. + * @param {Number} courseId Course ID the module belongs to. + * @return {Promise} Promise resolved with true if downloadable, resolved with false otherwise. + */ + self.isDownloadable = function(module, courseId) { + return $mmaModData.getDatabase(courseId, module.id, false, true).then(function(database) { + return $mmaModData.getDatabaseAccessInformation(database.id).then(function(accessData) { + // Check if database is restricted by time. + if (!accessData.timeavailable) { + var time = $mmUtil.timestamp(); + + // It is restricted, checking times. + if (database.timeavailablefrom && time < database.timeavailablefrom) { + return false; + } + if (database.timeavailableto && time > database.timeavailableto) { + return false; + } + } + return true; + }); + }); + }; + + /** + * Whether or not the module is enabled for the site. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#isEnabled + * @return {Boolean} + */ + self.isEnabled = function() { + return $mmaModData.isPluginEnabled(); + }; + + /** + * Prefetch the module. + * + * @module mm.addons.mod_data + * @ngdoc method + * @name $mmaModDataPrefetchHandler#prefetch + * @param {Object} module The module object returned by WS. + * @param {Number} courseId Course ID the module belongs to. + * @param {Boolean} single True if we're downloading a single module, false if we're downloading a whole section. + * @return {Promise} Promise resolved when all files have been downloaded. Data returned is not reliable. + */ + self.prefetch = function(module, courseId, single) { + return self.prefetchPackage(module, courseId, single, prefetchDatabase); + }; + + /** + * Prefetch a database. + * + * @param {Object} module The module object returned by WS. + * @param {Number} courseId Course ID the module belongs to. + * @param {Boolean} single True if we're downloading a single module, false if we're downloading a whole section. + * @param {String} siteId Site ID. + * @return {Promise} Promise resolved with an object with 'revision' and 'timemod'. + */ + function prefetchDatabase(module, courseId, single, siteId) { + // Prefetch the database data. + return $mmaModData.getDatabase(courseId, module.id, siteId).then(function(database) { + var promises = []; + + promises.push(self.getFiles(module, courseId, siteId).then(function(files) { + return $mmFilepool.addFilesToQueueByUrl(siteId, files, self.component, module.id); + })); + + promises.push($mmaModData.getDatabaseAccessInformation(database.id, false, true, siteId)); + + return $q.all(promises); + }).then(function() { + // Get revision and timemodified. + + var promises = []; + promises.push(self.getRevision(module, courseId)); + promises.push(self.getTimemodified(module, courseId)); + + // Return revision and timemodified. + return $q.all(promises).then(function(list) { + return { + revision: list[0], + timemod: list[1] + }; + }); + }); + } + + return self; +}); diff --git a/www/addons/mod/data/templates/index.html b/www/addons/mod/data/templates/index.html new file mode 100644 index 00000000000..fb4bf06e2ed --- /dev/null +++ b/www/addons/mod/data/templates/index.html @@ -0,0 +1,42 @@ + + {{ title }} + + + + + + + + + + + + + + + +
+
+ {{ description }} +
+
+
+ {{ 'mma.mod_data.notopenyet' | translate:{$a: timeAvailableFromReadable} }} +
+ +
+ {{ 'mma.mod_data.expired' | translate:{$a: timeAvailableToReadable} }} +
+ +
+ {{ 'mma.mod_data.entrieslefttoaddtoview' | translate:{$a: {entrieslefttoview: entriesLeftToView} } }} +
+ +
+ {{ 'mma.mod_data.entrieslefttoadd' | translate:{$a: {entriesleft: entriesLeftToAdd} } }} +
+ + +
+
+