diff --git a/www/addons/mod_glossary/controllers/entry.js b/www/addons/mod_glossary/controllers/entry.js new file mode 100644 index 00000000000..4d49a983c9f --- /dev/null +++ b/www/addons/mod_glossary/controllers/entry.js @@ -0,0 +1,94 @@ +// (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_glossary') + +/** + * Glossary entry controller. + * + * @module mm.addons.mod_glossary + * @ngdoc controller + * @name mmaModGlossaryEntryCtrl + */ +.controller('mmaModGlossaryEntryCtrl', function($scope, $stateParams, $mmaModGlossary, $translate, + mmUserProfileState) { + var entry = $stateParams.entry || {}, + courseid = $stateParams.courseid || 0, + glossary; + + // This is a coding error, for now the course ID is required here as we need it for the author link. + if (!courseid) { + notifyErrorOccured(); + return; + } + + $scope.refreshEntry = function() { + refreshEntry().finally(function() { + $scope.$broadcast('scroll.refreshComplete'); + }); + }; + + // Load the glossary first. + $mmaModGlossary.getGlossaryById(courseid, entry.glossaryid).then(function(gloss) { + glossary = gloss; + var displayFormat = glossary.displayformat; + + $scope.title = entry.concept; + $scope.entry = entry; + $scope.courseid = courseid; + $scope.userStateName = mmUserProfileState; + + if (displayFormat == 'fullwithauthor' || displayFormat == 'encyclopedia') { + $scope.showAuthor = true; + $scope.showDate = true; + + } else if (displayFormat == 'fullwithoutauthor') { + $scope.showAuthor = false; + $scope.showDate = true; + + // Default, and faq, simple, entrylist, continuous. + } else { + $scope.showAuthor = false; + $scope.showDate = false; + } + + $scope.loaded = true; + + // Log that the entry was viewed. + $mmaModGlossary.logEntryView(entry.id); + + }).catch(function() { + notifyErrorOccured(); + }); + + function fetchEntry() { + return $mmaModGlossary.getEntry(entry.id).then(function(result) { + $scope.entry = result.entry; + $scope.title = result.entry.concept; + }); + } + + function refreshEntry() { + return $mmaModGlossary.invalidateEntry(entry.id).then(function() { + return fetchEntry(); + }); + } + + function notifyErrorOccured() { + $scope.title = $translate.instant('mm.core.error'); + $scope.entry = false; + $scope.loaded = true; + } + +}); diff --git a/www/addons/mod_glossary/controllers/index.js b/www/addons/mod_glossary/controllers/index.js new file mode 100644 index 00000000000..1ea56937c78 --- /dev/null +++ b/www/addons/mod_glossary/controllers/index.js @@ -0,0 +1,260 @@ +// (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_glossary') + +/** + * Glossary index controller. + * + * @module mm.addons.mod_glossary + * @ngdoc controller + * @name mmaModGlossaryIndexCtrl + */ +.controller('mmaModGlossaryIndexCtrl', function($q, $scope, $stateParams, $ionicPopover, $mmUtil, $mmaModGlossary, + $ionicScrollDelegate) { + + var module = $stateParams.module || {}, + courseId = $stateParams.courseid, + glossary, + noop = function(){}, + limitFrom = 0, + limitNum = 25, + popover, + viewMode, // The archetype of view (letter, date, author, cat). + fetchMode = 'letter_all', // Default. + fetchFunction, + fetchInvalidate, + fetchArguments, + popoverScope = $scope.$new(true), + browseModes = [ + { + key: 'letter_all', + langkey: 'mma.mod_glossary.byalphabet' + }, + { + key: 'search', + langkey: 'mma.mod_glossary.bysearch' + } + ]; + + $scope.title = module.name; + $scope.description = module.description; + $scope.externalUrl = module.url; + $scope.courseid = courseId; + $scope.loaded = false; + $scope.entries = []; + $scope.getDivider = noop; + $scope.showDivider = noop; + $scope.canLoadMore = false; + $scope.searchData = { + searchQuery: '' + }; + + $scope.loadMoreEntries = function() { + loadMoreEntries().finally(function() { + $scope.$broadcast('scroll.infiniteScrollComplete'); + }); + }; + $scope.refreshEntries = function() { + refreshEntries().finally(function() { + $scope.$broadcast('scroll.refreshComplete'); + }); + }; + + $scope.pickMode = function(e) { + popoverScope.data.selectedMode = fetchMode; + popover.show(e); + }; + + $scope.search = function(query) { + fetchArguments = [glossary.id, query, 1, 'CONCEPT', 'ASC']; + fetchEntries(); + }; + + $scope.trackBy = function(entry) { + return fetchMode + ':' + entry.id; + }; + + // Controller run. + $mmaModGlossary.getGlossary(courseId, module.id).then(function(mod) { + glossary = mod; + + // Preparing browse modes. + if (glossary.browsemodes.indexOf('date') >= 0) { + browseModes.push({key: 'newest_first', langkey: 'mma.mod_glossary.bynewestfirst'}); + browseModes.push({key: 'recently_updated', langkey: 'mma.mod_glossary.byrecentlyupdated'}); + } + if (glossary.browsemodes.indexOf('author') >= 0) { + browseModes.push({key: 'author_all', langkey: 'mma.mod_glossary.byauthor'}); + } + + // Preparing the popover. + popoverScope.modes = browseModes; + popoverScope.modePicked = function(mode) { + $ionicScrollDelegate.$getByHandle('mmaModGlossaryIndex').scrollTop(false); + if (switchMode(mode)) { + $scope.loaded = false; + fetchEntries().finally(function() { + $scope.loaded = true; + }); + } else { + // If it's not an instant search, then we reset the values. + $scope.loaded = true; + $scope.entries = []; + $scope.canLoadMore = false; + $scope.showNoEntries = false; + } + popover.hide(); + }; + popoverScope.data = { selectedMode: '' }; + $ionicPopover.fromTemplateUrl('addons/mod_glossary/templates/mode_picker.html', { + scope: popoverScope + }).then(function(po) { + popover = po; + }); + $scope.$on('$destroy', function() { + popover.remove(); + popoverScope.$destroy(); + }); + + // Preparing the initial mode. + switchMode(); + + // Do not return the promise here, the error modal is already handled. + fetchEntries().then(function() { + // After a successful fetch, the glossary can be considered as 'viewed'. + $mmaModGlossary.logView(glossary.id, viewMode); + }).finally(function() { + $scope.loaded = true; + }); + }).catch(function() { + $mmUtil.showErrorModal('mma.mod_glossary.errorloadingglossary', true); + $scope.loaded = true; + }); + + // Controller library. + function fetchEntries(append) { + if (!append) { + limitFrom = 0; + } + var args = angular.extend([], fetchArguments); + args.push(limitFrom); + args.push(limitNum); + + return fetchFunction.apply(this, args).then(function(result) { + if (append) { + $scope.entries = $scope.entries.concat(result.entries); + } else { + $scope.entries = result.entries; + } + $scope.canLoadMore = (limitFrom + limitNum) < result.count; + $scope.showNoEntries = result.count <= 0; + }).catch(function() { + $mmUtil.showErrorModal('mma.mod_glossary.errorloadingentries', true); + return $q.reject(); + }); + } + + function refreshEntries() { + if (fetchMode == 'search' && !$scope.searchQuery) { + // Ignore search mode that is not set yet. + return $q.when(); + } + var args = angular.extend([], fetchArguments); + return fetchInvalidate.apply(this, args).then(function() { + limitFrom = 0; + return fetchEntries(); + }); + } + + function loadMoreEntries() { + limitFrom += limitNum; + return fetchEntries(true); + } + + function switchMode(mode) { + if (mode == fetchMode) { + return false; + } + + var instantFetch = true; + fetchMode = mode; + $scope.isSearch = false; + + // Browse by author. + if (mode == 'author_all') { + viewMode = 'author'; + fetchFunction = $mmaModGlossary.getEntriesByAuthor; + fetchInvalidate = $mmaModGlossary.invalidateEntriesByAuthor; + fetchArguments = [glossary.id, 'ALL', 'LASTNAME', 'ASC']; + $scope.getDivider = function(entry) { + return entry.userfullname; + }; + $scope.showDivider = function(entry, previous) { + if (typeof previous === 'undefined') { + return true; + } + return entry.userid != previous.userid; + }; + + // Newest first. + } else if (mode == 'newest_first') { + viewMode = 'date'; + fetchFunction = $mmaModGlossary.getEntriesByDate; + fetchInvalidate = $mmaModGlossary.invalidateEntriesByDate; + fetchArguments = [glossary.id, 'CREATION', 'DESC']; + $scope.getDivider = noop; + $scope.showDivider = function() { return false; }; + + // Recently updated. + } else if (mode == 'recently_updated') { + viewMode = 'date'; + fetchFunction = $mmaModGlossary.getEntriesByDate; + fetchInvalidate = $mmaModGlossary.invalidateEntriesByDate; + fetchArguments = [glossary.id, 'UPDATE', 'DESC']; + $scope.getDivider = noop; + $scope.showDivider = function() { return false; }; + + // Search for entries. + } else if (mode == 'search') { + viewMode = 'search'; + fetchFunction = $mmaModGlossary.getEntriesBySearch; + fetchInvalidate = $mmaModGlossary.invalidateEntriesBySearch; + fetchArguments = false; // Dynamically set later. + $scope.isSearch = true; + $scope.getDivider = noop; + $scope.showDivider = function() { return false; }; + instantFetch = false; + + // Consider it is 'letter_all'. + } else { + viewMode = 'letter'; + fetchMode = 'letter_all'; + fetchFunction = $mmaModGlossary.getEntriesByLetter; + fetchInvalidate = $mmaModGlossary.invalidateEntriesByLetter; + fetchArguments = [glossary.id, 'ALL']; + $scope.getDivider = function(entry) { + return entry.concept.substr(0, 1).toUpperCase(); + }; + $scope.showDivider = function(entry, previous) { + if (typeof previous === 'undefined') { + return true; + } + return $scope.getDivider(entry) != $scope.getDivider(previous); + }; + } + + return instantFetch; + } +}); diff --git a/www/addons/mod_glossary/lang/en.json b/www/addons/mod_glossary/lang/en.json new file mode 100644 index 00000000000..29bd6a576bb --- /dev/null +++ b/www/addons/mod_glossary/lang/en.json @@ -0,0 +1,14 @@ +{ + "browsemode": "Browse entries", + "byalphabet": "Alphabetically", + "byauthor": "Group by author", + "bynewestfirst": "Newest first", + "byrecentlyupdated": "Recently updated", + "bysearch": "Search", + "entrypendingapproval": "This entry is pending approval.", + "errorloadingentries": "An error occured while loading entries.", + "errorloadingentry": "An error occured while loading the entry.", + "errorloadingglossary": "An error occured while loading the glossary.", + "noentriesfound": "No entries were found.", + "searchquery": "Search query" +} diff --git a/www/addons/mod_glossary/main.js b/www/addons/mod_glossary/main.js new file mode 100644 index 00000000000..df8d1d04f74 --- /dev/null +++ b/www/addons/mod_glossary/main.js @@ -0,0 +1,53 @@ +// (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_glossary', ['mm.core']) + +.config(function($stateProvider) { + + $stateProvider + + .state('site.mod_glossary', { + url: '/mod_glossary', + params: { + module: null, + courseid: null + }, + views: { + 'site': { + controller: 'mmaModGlossaryIndexCtrl', + templateUrl: 'addons/mod_glossary/templates/index.html' + } + } + }) + + .state('site.mod_glossary-entry', { + url: '/mod_glossary-entry', + params: { + courseid: null, + entry: null + }, + views: { + 'site': { + controller: 'mmaModGlossaryEntryCtrl', + templateUrl: 'addons/mod_glossary/templates/entry.html' + } + } + }); + +}) + +.config(function($mmCourseDelegateProvider) { + $mmCourseDelegateProvider.registerContentHandler('mmaModGlossary', 'glossary', '$mmaModGlossaryCourseContentHandler'); +}); diff --git a/www/addons/mod_glossary/services/course_content_handler.js b/www/addons/mod_glossary/services/course_content_handler.js new file mode 100644 index 00000000000..39f43600289 --- /dev/null +++ b/www/addons/mod_glossary/services/course_content_handler.js @@ -0,0 +1,61 @@ +// (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_glossary') + +/** + * Mod glossary course content handler. + * + * @module mm.addons.mod_glossary + * @ngdoc service + * @name $mmaModGlossaryCourseContentHandler + */ +.factory('$mmaModGlossaryCourseContentHandler', function($mmCourse, $mmSite, $state) { + var self = {}; + + /** + * Whether or not the module is enabled for the site. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryCourseContentHandler#isEnabled + * @return {Boolean} + */ + self.isEnabled = function() { + // This function was introduced along with all the other required ones. + return $mmSite.wsAvailable('mod_glossary_get_glossaries_by_courses'); + }; + + /** + * Get the controller. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryCourseContentHandler#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('glossary'); + $scope.title = module.name; + $scope.action = function(e) { + $state.go('site.mod_glossary', {module: module, courseid: courseid}); + }; + }; + }; + + return self; +}); diff --git a/www/addons/mod_glossary/services/glossary.js b/www/addons/mod_glossary/services/glossary.js new file mode 100644 index 00000000000..5b39a9bb653 --- /dev/null +++ b/www/addons/mod_glossary/services/glossary.js @@ -0,0 +1,452 @@ +// (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_glossary') + +/** + * Glossary service. + * + * @module mm.addons.mod_glossary + * @ngdoc service + * @name $mmaModGlossary + */ +.factory('$mmaModGlossary', function($mmSite, $q) { + var self = {}; + + /** + * Get the course glossary cache key. + * + * @param {Number} courseId + * @return {String} + * @protected + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#_getCourseGlossariesCacheKey + */ + self._getCourseGlossariesCacheKey = function(courseId) { + return 'mmaModGlossary:courseGlossaries:' + courseId; + }; + + /** + * Get all the glossaries in a course. + * + * @param {Number} courseId + * @return {Promise} resolved with the glossaries + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#getCourseGlossaries + */ + self.getCourseGlossaries = function(courseId) { + var params = { + courseids: [courseId] + }, + preSets = { + cacheKey: self._getCourseGlossariesCacheKey(courseId) + }; + return $mmSite.read('mod_glossary_get_glossaries_by_courses', params, preSets).then(function(result) { + return result.glossaries; + }); + }; + + /** + * Get the course glossary cache key. + * + * @param {Number} courseId + * @return {Promise} + * @protected + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#invalidateCourseGlossaries + */ + self.invalidateCourseGlossaries = function(courseId) { + var key = self._getCourseGlossariesCacheKey(courseId); + return $mmSite.invalidateWsCacheForKey(key); + }; + + /** + * Get the entries by author cache key. + * + * @param {Number} glossaryId + * @param {String} letter + * @param {String} field + * @param {String} sort + * @return {String} + * @protected + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#_getEntriesByAuthorCacheKey + */ + self._getEntriesByAuthorCacheKey = function(glossaryId, letter, field, sort) { + return 'mmaModGlossary:entriesByAuthor:' + glossaryId + ":" + letter + ":" + field + ":" + sort; + }; + + /** + * Get entries by author. + * + * @param {Number} glossaryId + * @param {String} letter + * @param {String} field + * @param {String} sort + * @param {Number} from + * @param {Number} limit + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#getEntriesByAuthor + */ + self.getEntriesByAuthor = function(glossaryId, letter, field, sort, from, limit) { + var params = { + id: glossaryId, + letter: letter, + field: field, + sort: sort, + from: from, + limit: limit + }, + preSets = { + cacheKey: self._getEntriesByAuthorCacheKey(glossaryId, letter, field, sort) + }; + + return $mmSite.read('mod_glossary_get_entries_by_author', params, preSets); + }; + + /** + * Invalidate cache of entries by author. + * + * @param {Number} glossaryId + * @param {String} letter + * @param {String} field + * @param {String} sort + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#invalidateEntriesByAuthor + */ + self.invalidateEntriesByAuthor = function(glossaryId, letter, field, sort) { + var key = self._getEntriesByAuthorCacheKey(glossaryId, letter, field, sort); + return $mmSite.invalidateWsCacheForKey(key); + }; + + /** + * Get the entries by date cache key. + * + * @param {Number} glossaryId + * @param {String} order + * @param {String} sort + * @return {String} + * @protected + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#_getEntriesByDateCacheKey + */ + self._getEntriesByDateCacheKey = function(glossaryId, order, sort) { + return 'mmaModGlossary:entriesByDate:' + glossaryId + ":" + order + ":" + sort; + }; + + /** + * Get entries by date. + * + * @param {Number} glossaryId + * @param {String} order + * @param {String} sort + * @param {Number} from + * @param {Number} limit + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#getEntriesByDate + */ + self.getEntriesByDate = function(glossaryId, order, sort, from, limit) { + var params = { + id: glossaryId, + order: order, + sort: sort, + from: from, + limit: limit + }, + preSets = { + cacheKey: self._getEntriesByDateCacheKey(glossaryId, order, sort) + }; + + return $mmSite.read('mod_glossary_get_entries_by_date', params, preSets); + }; + + /** + * Invalidate cache of entries by date. + * + * @param {Number} glossaryId + * @param {String} letter + * @param {String} field + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#invalidateEntriesByDate + */ + self.invalidateEntriesByDate = function(glossaryId, order, sort) { + var key = self._getEntriesByDateCacheKey(glossaryId, order, sort); + return $mmSite.invalidateWsCacheForKey(key); + }; + + /** + * Get the entries by letter cache key. + * + * @param {Number} glossaryId + * @param {String} letter + * @return {String} + * @protected + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#_getEntriesByLetterCacheKey + */ + self._getEntriesByLetterCacheKey = function(glossaryId, letter) { + return 'mmaModGlossary:entriesByLetter:' + glossaryId + ":" + letter; + }; + + /** + * Get entries by letter. + * + * @param {Number} glossaryId + * @param {String} letter + * @param {Number} from + * @param {Number} limit + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#getEntriesByLetter + */ + self.getEntriesByLetter = function(glossaryId, letter, from, limit) { + var params = { + id: glossaryId, + letter: letter, + from: from, + limit: limit + }, + preSets = { + cacheKey: self._getEntriesByLetterCacheKey(glossaryId, letter) + }; + + return $mmSite.read('mod_glossary_get_entries_by_letter', params, preSets); + }; + + /** + * Invalidate cache of entries by letter. + * + * @param {Number} glossaryId + * @param {String} letter + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#invalidateEntriesByLetter + */ + self.invalidateEntriesByLetter = function(glossaryId, letter) { + var key = self._getEntriesByLetterCacheKey(glossaryId, letter); + return $mmSite.invalidateWsCacheForKey(key); + }; + + /** + * Get the entries by search cache key. + * + * @param {Number} glossaryId + * @param {String} query + * @param {Boolean} fullsearch + * @param {String} order + * @param {String} sort + * @return {String} + * @protected + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#_getEntriesBySearchCacheKey + */ + self._getEntriesBySearchCacheKey = function(glossaryId, query, fullsearch, order, sort) { + return 'mmaModGlossary:entriesBySearch:' + glossaryId + ":" + fullsearch + ":" + order + ":" + sort + ":" + query; + }; + + /** + * Get entries by search. + * + * @param {Number} glossaryId + * @param {String} query + * @param {Boolean} fullsearch + * @param {String} order + * @param {String} sort + * @param {Number} from + * @param {Number} limit + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#getEntriesBySearch + */ + self.getEntriesBySearch = function(glossaryId, query, fullsearch, order, sort, from, limit) { + var params = { + id: glossaryId, + query: query, + fullsearch: fullsearch, + order: order, + sort: sort, + from: from, + limit: limit + }, + preSets = { + cacheKey: self._getEntriesBySearchCacheKey(glossaryId, query, fullsearch, order, sort) + }; + + return $mmSite.read('mod_glossary_get_entries_by_search', params, preSets); + }; + + /** + * Invalidate cache of entries by search. + * + * @param {Number} glossaryId + * @param {String} query + * @param {Boolean} fullsearch + * @param {String} order + * @param {String} sort + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#invalidateEntriesBySearch + */ + self.invalidateEntriesBySearch = function(glossaryId, query, fullsearch, order, sort) { + var key = self._getEntriesBySearchCacheKey(glossaryId, query, fullsearch, order, sort); + return $mmSite.invalidateWsCacheForKey(key); + }; + + /** + * Get an entry by ID cache key. + * + * @param {Number} id + * @return {String} + * @protected + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#_getEntryCacheKey + */ + self._getEntryCacheKey = function(id) { + return 'mmaModGlossary:getEntry:' + id; + }; + + /** + * Get one entry by ID. + * + * @param {Number} id + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#getEntry + */ + self.getEntry = function(id) { + var params = { + id: id + }, + preSets = { + cacheKey: self._getEntryCacheKey(id) + }; + + return $mmSite.read('mod_glossary_get_entry_by_id', params, preSets); + }; + + /** + * Invalidate cache of entry by ID. + * + * @param {Number} id + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#invalidateEntry + */ + self.invalidateEntry = function(id) { + var key = self._getEntryCacheKey(id); + return $mmSite.invalidateWsCacheForKey(key); + }; + + /** + * Get one glossary by cmID. + * + * @param {Number} courseId + * @param {Number} cmid + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#getGlossary + */ + self.getGlossary = function(courseId, cmid) { + return self.getCourseGlossaries(courseId).then(function(glossaries) { + var result = $q.reject(); + angular.forEach(glossaries, function(glossary) { + if (glossary.coursemodule == cmid) { + result = glossary; + } + }); + return result; + }); + }; + + /** + * Get one glossary by glossary ID. + * + * @param {Number} courseId + * @param {Number} id + * @return {Promise} + * @ngdoc method + * @module mm.addons.mod_glossary + * @name $mmaModGlossary#getGlossaryById + */ + self.getGlossaryById = function(courseId, id) { + return self.getCourseGlossaries(courseId).then(function(glossaries) { + var result = $q.reject(); + angular.forEach(glossaries, function(glossary) { + if (glossary.id == id) { + result = glossary; + } + }); + return result; + }); + }; + + /** + * Report a glossary as being viewed. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossary#logView + * @param {Number} id Glossary ID. + * @param {String} mode The mode in which the glossary was viewed. + * @return {Promise} Promise resolved when the WS call is successful. + */ + self.logView = function(id, mode) { + var params = { + id: id, + mode: mode + }; + return $mmSite.write('mod_glossary_view_glossary', params); + }; + + /** + * Report a glossary entry as being viewed. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossary#logEntryView + * @param {Number} id Entry ID. + * @return {Promise} Promise resolved when the WS call is successful. + */ + self.logEntryView = function(id) { + var params = { + id: id + }; + return $mmSite.write('mod_glossary_view_entry', params); + }; + + return self; +}); diff --git a/www/addons/mod_glossary/templates/entry.html b/www/addons/mod_glossary/templates/entry.html new file mode 100644 index 00000000000..8d97ef4cd1d --- /dev/null +++ b/www/addons/mod_glossary/templates/entry.html @@ -0,0 +1,42 @@ + + {{ title }} + + + + + + +
+ + +

{{ entry.concept }}

+

{{ entry.timemodified | mmDateDayOrTime }}

+

{{ entry.userfullname }}

+
+ +
+

{{ entry.concept }}

+

{{ entry.timemodified | mmDateDayOrTime }}

+
+ +
+ {{ entry.definition }} +
+ +
+
+ +
+

{{ 'mma.mod_glossary.entrypendingapproval' | translate }}

+
+
+ +
+

+ {{ 'mma.mod_glossary.errorloadingentry' | translate }} +

+
+ +
+
+
diff --git a/www/addons/mod_glossary/templates/index.html b/www/addons/mod_glossary/templates/index.html new file mode 100644 index 00000000000..0e7756b6548 --- /dev/null +++ b/www/addons/mod_glossary/templates/index.html @@ -0,0 +1,51 @@ + + {{ title }} + + + + + + + + + + + + + +
+
+
+ + +
+
+
+ + +
+
+ +
+

{{getDivider(entries[$index])}}

+
+ + +

{{entry.concept}}

+
+
+
+ +
+

{{ 'mma.mod_glossary.noentriesfound' | translate }}

+
+ + + +
+
+
+
diff --git a/www/addons/mod_glossary/templates/mode_picker.html b/www/addons/mod_glossary/templates/mode_picker.html new file mode 100644 index 00000000000..41390654adb --- /dev/null +++ b/www/addons/mod_glossary/templates/mode_picker.html @@ -0,0 +1,9 @@ + + +
+ + {{ mode.langkey | translate }} + +
+
+
diff --git a/www/core/directives/splitviewlink.js b/www/core/directives/splitviewlink.js index ef1c1ccb2aa..f60b5226ea1 100644 --- a/www/core/directives/splitviewlink.js +++ b/www/core/directives/splitviewlink.js @@ -33,7 +33,7 @@ angular.module('mm.core') .directive('mmSplitViewLink', function($log, $ionicPlatform, $state, $mmApp) { $log = $log.getInstance('mmSplitViewLink'); - var srefRegex = new RegExp(/([^\(]*)(\(([^\)]*)\))?/); + var srefRegex = new RegExp(/([^\(]*)(\((.*)\))?$/); /** * Create a new state for tablet view (split-view). The state created will be exactly the same as the target state