From 8c7dd6589952dd72b13cbe0235c6864631ce2b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Fri, 13 Jan 2017 11:43:18 +0100 Subject: [PATCH 1/3] MOBILE-1966 attachment: Uploading files if undefined max submissions --- www/core/directives/attachments.js | 1 + www/core/templates/attachments.html | 10 +++++----- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/www/core/directives/attachments.js b/www/core/directives/attachments.js index 3b88aaa9d33..075c1f3af1a 100644 --- a/www/core/directives/attachments.js +++ b/www/core/directives/attachments.js @@ -70,6 +70,7 @@ angular.module('mm.core') if (typeof scope.maxSubmissions == 'undefined' || scope.maxSubmissions < 0) { scope.maxSubmissions = $translate.instant('mm.core.unknown'); + scope.unlimitedFiles = true; } scope.add = function() { diff --git a/www/core/templates/attachments.html b/www/core/templates/attachments.html index e151729006d..c830b790650 100644 --- a/www/core/templates/attachments.html +++ b/www/core/templates/attachments.html @@ -1,6 +1,6 @@ -

+

{{ 'mm.core.maxsizeandattachments' | translate:{$a: {size: maxSizeReadable, attachments: maxSubmissions} } }} -

+
@@ -8,8 +8,8 @@
-
- - {{ 'mm.fileuploader.addfiletext' | translate }} + \ No newline at end of file From 91f9a83c1e730240118d82856ebd14def4009406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Tue, 7 Feb 2017 15:11:22 +0100 Subject: [PATCH 2/3] MOBILE-1966 multipleselect: Load selected options --- www/core/directives/multipleselect.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/www/core/directives/multipleselect.js b/www/core/directives/multipleselect.js index f289cbe17cd..4313f0d262d 100644 --- a/www/core/directives/multipleselect.js +++ b/www/core/directives/multipleselect.js @@ -53,7 +53,7 @@ angular.module('mm.core') strSeparator = $translate.instant('mm.core.elementseparator') + " "; scope.optionsRender = []; - scope.selectedOptions = ""; + scope.selectedOptions = getSelectedOptionsText(); element.on('click', function(e) { e.preventDefault(); @@ -81,24 +81,31 @@ angular.module('mm.core') }); scope.saveOptions = function() { - var selected = []; angular.forEach(scope.optionsRender, function (tempOption){ for (var j = 0; j < scope.options.length; j++) { var option = scope.options[j]; if (option[keyProperty] == tempOption.key) { option[selectedProperty] = tempOption.selected; - if (tempOption.selected) { - selected.push(tempOption.value); - } return; } } }); - scope.selectedOptions = selected.join(strSeparator); + scope.selectedOptions = getSelectedOptionsText(); scope.closeModal(); }; + // Get string for selected options to be shown. + function getSelectedOptionsText() { + var selected = scope.options.filter(function(option) { + return !!option[selectedProperty]; + }).map(function(option) { + return option[valueProperty]; + }); + + return selected.join(strSeparator); + } + scope.closeModal = function(){ scope.modal.hide(); }; From 242f0e30392a49c5a6757a93322bbadce7d138aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= Date: Mon, 16 Jan 2017 14:54:05 +0100 Subject: [PATCH 3/3] MOBILE-1966 glossary: Add attachments field --- www/addons/mod/glossary/controllers/edit.js | 84 ++++++++-- www/addons/mod/glossary/lang/en.json | 1 + www/addons/mod/glossary/services/glossary.js | 41 +++-- .../mod/glossary/services/glossary_offline.js | 68 ++++++-- .../mod/glossary/services/glossary_sync.js | 67 +++++++- www/addons/mod/glossary/services/helper.js | 150 ++++++++++++++++++ www/addons/mod/glossary/templates/edit.html | 2 + 7 files changed, 368 insertions(+), 45 deletions(-) create mode 100644 www/addons/mod/glossary/services/helper.js diff --git a/www/addons/mod/glossary/controllers/edit.js b/www/addons/mod/glossary/controllers/edit.js index b1bdad6fce8..c3d61ebabf1 100644 --- a/www/addons/mod/glossary/controllers/edit.js +++ b/www/addons/mod/glossary/controllers/edit.js @@ -22,7 +22,7 @@ angular.module('mm.addons.mod_glossary') * @name mmaModGlossaryEditCtrl */ .controller('mmaModGlossaryEditCtrl', function($stateParams, $scope, mmaModGlossaryComponent, $mmUtil, $q, $mmaModGlossary, $mmText, - $translate, $ionicHistory, $mmEvents, mmaModGlossaryAddEntryEvent, $mmaModGlossaryOffline) { + $translate, $ionicHistory, $mmEvents, mmaModGlossaryAddEntryEvent, $mmaModGlossaryOffline, $mmaModGlossaryHelper) { var module = $stateParams.module, courseId = $stateParams.courseid, @@ -46,6 +46,7 @@ angular.module('mm.addons.mod_glossary') casesensitive: false, fullmatch: false }; + $scope.attachments = []; if (entry) { $scope.entry.concept = entry.concept || ''; @@ -54,40 +55,70 @@ angular.module('mm.addons.mod_glossary') $scope.options.categories = entry.options.categories || null; $scope.options.aliases = entry.options.aliases || ""; $scope.options.usedynalink = !!entry.options.usedynalink || glossary.usedynalink; - $scope.options.casesensitive = !!entry.options.casesensitive + $scope.options.casesensitive = !!entry.options.casesensitive; $scope.options.fullmatch = !!entry.options.fullmatch; } + + // Treat offline attachments if any. + if (entry.attachments && entry.attachments.offline) { + $mmaModGlossaryHelper.getStoredFiles(glossaryId, entry.concept).then(function(files) { + $scope.attachments = files; + }); + } } // Block leaving the view, we want to show a confirm to the user if there's unsaved data. $mmUtil.blockLeaveView($scope, cancel); // Fetch Glossary data. - function fetchGlossaryData(refresh) { + function fetchGlossaryData() { return $mmaModGlossary.getAllCategories(glossaryId).then(function(categories) { $scope.categories = categories; + + if ($scope.options.categories) { + var cats = $scope.options.categories.split(","); + angular.forEach(cats, function(catId) { + angular.forEach($scope.categories, function(category) { + if (category.id == catId) { + category.selected = true; + } + }); + }); + } }); } // Just ask to confirm the lost of data. function cancel() { - if (!$scope.entry.text && !$scope.entry.concept) { - return $q.when(); + var promise; + + if (!$mmaModGlossaryHelper.hasEntryDataChanged($scope.entry, $scope.attachments)) { + promise = $q.when(); } else { // Show confirmation if some data has been modified. - return $mmUtil.showConfirm($translate('mm.core.confirmcanceledit')); + promise = $mmUtil.showConfirm($translate('mm.core.confirmcanceledit')); } + + return promise.then(function() { + // Delete the local files from the tmp folder. + $mmaModGlossaryHelper.clearTmpFiles($scope.attachments); + }); } $scope.save = function() { var concept = $scope.entry.concept, - definition = $scope.entry.text; + definition = $scope.entry.text, + modal, + attachments, + saveOffline = false; if (!concept || !definition) { $mmUtil.showErrorModal('mma.mod_glossary.fillfields', true); return; } + modal = $mmUtil.showModalLoading('mm.core.sending', true); + // Check if rich text editor is enabled or not. $mmUtil.isRichTextEditorEnabled().then(function(enabled) { if (!enabled) { @@ -101,7 +132,19 @@ angular.module('mm.addons.mod_glossary') } return $q.when(); - }).then(function(entryId) { + }).then(function() { + attachments = $scope.attachments; + + // Upload attachments first if any. + if (attachments.length) { + return $mmaModGlossaryHelper.uploadOrStoreFiles(glossaryId, concept, attachments, false) + .catch(function() { + // Cannot upload them in online, save them in offline. + saveOffline = true; + return $mmaModGlossaryHelper.uploadOrStoreFiles(glossaryId, concept, attachments, true); + }); + } + }).then(function(attach) { var cats = $scope.categories.filter(function(category) { return category.selected; }).map(function(category) { @@ -120,24 +163,43 @@ angular.module('mm.addons.mod_glossary') options.fullmatch = $scope.options.fullmatch ? 1 : 0; } } - return $mmaModGlossary.addEntry(glossaryId, concept, courseId, definition, options); + + if (saveOffline) { + // Save entry in offline. + return $mmaModGlossaryOffline.saveAddEntry(glossaryId, concept, definition, courseId, options, attach).then(function() { + // Don't return anything. + }); + } else { + // Try to send it to server. + // Don't allow offline if there are attachments since they were uploaded fine. + return $mmaModGlossary.addEntry(glossaryId, concept, definition, courseId, options, attach, undefined, + !attachments.length); + } }).then(function(entryId) { + if (entryId) { + $scope.entry.id = entryId; + // Data sent to server, delete stored files (if any). + $mmaModGlossaryHelper.deleteStoredFiles(glossaryId, concept); + } $scope.entry.glossaryid = glossaryId; - $scope.entry.id = entryId; $scope.entry.definition = definition; return returnToEntryList(); }).catch(function(error) { $mmUtil.showErrorModalDefault(error, 'mma.mod_glossary.cannoteditentry', true); + }).finally(function() { + modal.dismiss(); }); }; - function returnToEntryList(entryId) { + function returnToEntryList() { var data = { glossaryid: glossaryId, cmid: cmid, entry: $scope.entry }; + $mmaModGlossaryHelper.clearTmpFiles($scope.attachments); + $mmEvents.trigger(mmaModGlossaryAddEntryEvent, data); // Go back to discussions list. diff --git a/www/addons/mod/glossary/lang/en.json b/www/addons/mod/glossary/lang/en.json index 67dc3be65d5..a5e8b2a0f3d 100644 --- a/www/addons/mod/glossary/lang/en.json +++ b/www/addons/mod/glossary/lang/en.json @@ -1,6 +1,7 @@ { "addentry": "Add a new entry", "aliases": "Keyword(s)", + "attachment": "Attachment", "browsemode": "Browse entries", "byalphabet": "Alphabetically", "byauthor": "Group by author", diff --git a/www/addons/mod/glossary/services/glossary.js b/www/addons/mod/glossary/services/glossary.js index 60bee7191b9..8bf843d50e6 100644 --- a/www/addons/mod/glossary/services/glossary.js +++ b/www/addons/mod/glossary/services/glossary.js @@ -630,18 +630,20 @@ angular.module('mm.addons.mod_glossary') * @module mm.addons.mod_glossary * @ngdoc method * @name $mmaModGlossary#addEntry - * @param {Number} glossaryId Glossary ID. - * @param {String} concept Glossary entry concept. - * @param {String} definition Glossary entry concept definition. - * @param {Number} courseId Course ID of the glossary. - * @param {Array} [options] Array of options for the entry. - * @param {String} [siteId] Site ID. If not defined, current site. + * @param {Number} glossaryId Glossary ID. + * @param {String} concept Glossary entry concept. + * @param {String} definition Glossary entry concept definition. + * @param {Number} courseId Course ID of the glossary. + * @param {Array} [options] Array of options for the entry. + * @param {Mixed} [attach] Attachments ID if sending online, result of $mmFileUploader#storeFilesToUpload otherwise. + * @param {String} [siteId] Site ID. If not defined, current site. + * @param {Boolean} allowOffline True if it can be stored in offline, false otherwise. * @return {Promise} Promise resolved with entry ID if entry was created in server, false if stored in device. */ - self.addEntry = function(glossaryId, concept, definition, courseId, options, siteId) { + self.addEntry = function(glossaryId, concept, definition, courseId, options, attach, siteId, allowOffline) { siteId = siteId || $mmSite.getId(); - if (!$mmApp.isOnline()) { + if (!$mmApp.isOnline() && allowOffline) { // App is offline, store the action. return storeOffline(); } @@ -649,22 +651,23 @@ angular.module('mm.addons.mod_glossary') // Discard stored content for this entry. If it exists it means the user is editing it. return $mmaModGlossaryOffline.deleteAddEntry(glossaryId, concept, siteId).then(function() { // Try to add it in online. - return self.addEntryOnline(glossaryId, concept, definition, options, siteId).then(function(entryId) { + return self.addEntryOnline(glossaryId, concept, definition, options, attach, siteId).then(function(entryId) { return entryId; }).catch(function(error) { - if (error && error.wserror) { - // The WebService has thrown an error, this means that responses cannot be deleted. - return $q.reject(error.error); - } else { + if (allowOffline && error && !error.wserror) { // Couldn't connect to server, store in offline. return storeOffline(); + } else { + // The WebService has thrown an error or offline not supported, reject. + return $q.reject(error.error); } }); }); // Convenience function to store a new page to be synchronized later. function storeOffline() { - return $mmaModGlossaryOffline.saveAddEntry(glossaryId, concept, definition, courseId, options, siteId).then(function() { + return $mmaModGlossaryOffline.saveAddEntry(glossaryId, concept, definition, courseId, options, attach, + siteId).then(function() { return false; }); } @@ -680,12 +683,13 @@ angular.module('mm.addons.mod_glossary') * @param {String} concept Glossary entry concept. * @param {String} definition Glossary entry concept definition. * @param {Array} [options] Array of options for the entry. + * @param {Number} [attachId] Attachments ID (if any attachment). * @param {String} [siteId] Site ID. If not defined, current site. * @return {Promise} Promise resolved if created, rejected otherwise. Reject param is an object with: * - error: The error message. * - wserror: True if it's an error returned by the WebService, false otherwise. */ - self.addEntryOnline = function(glossaryId, concept, definition, options, siteId) { + self.addEntryOnline = function(glossaryId, concept, definition, options, attachId, siteId) { return $mmSitesManager.getSite(siteId).then(function(site) { var params = { glossaryid: glossaryId, @@ -697,6 +701,13 @@ angular.module('mm.addons.mod_glossary') params.options = $mmUtil.objectToArrayOfObjects(options, 'name', 'value'); } + if (attachId) { + params.options.push({ + name: 'attachmentsid', + value: attachId + }); + } + return addEntryOnline(site, params).then(function(response) { if (response.entryid) { return response.entryid; diff --git a/www/addons/mod/glossary/services/glossary_offline.js b/www/addons/mod/glossary/services/glossary_offline.js index ad2409c4474..73f1c325b66 100644 --- a/www/addons/mod/glossary/services/glossary_offline.js +++ b/www/addons/mod/glossary/services/glossary_offline.js @@ -48,7 +48,7 @@ angular.module('mm.addons.mod_glossary') * @ngdoc service * @name $mmaModGlossaryOffline */ -.factory('$mmaModGlossaryOffline', function($mmSitesManager, $log, mmaModGlossaryAddEntryStore) { +.factory('$mmaModGlossaryOffline', function($mmSitesManager, $log, mmaModGlossaryAddEntryStore, $mmFS) { $log = $log.getInstance('$mmaModGlossaryOffline'); var self = {}; @@ -127,21 +127,21 @@ angular.module('mm.addons.mod_glossary') * @module mm.addons.mod_glossary * @ngdoc method * @name $mmaModGlossaryOffline#saveAddEntry - * @param {Number} glossaryId Glossary ID. - * @param {String} concept Glossary entry concept. - * @param {String} definition Glossary entry concept definition. - * @param {Number} courseId Course ID of the glossary. - * @param {Array} [options] Array of options for the entry. - * @param {String} [siteId] Site ID. If not defined, current site. - * @param {Number} [userId] User the entry belong to. If not defined, current user in site. - * @return {Promise} Promise resolved if stored, rejected if failure. + * @param {Number} glossaryId Glossary ID. + * @param {String} concept Glossary entry concept. + * @param {String} definition Glossary entry concept definition. + * @param {Number} courseId Course ID of the glossary. + * @param {Array} [options] Array of options for the entry. + * @param {Object} [attach] Result of $mmFileUploader#storeFilesToUpload for attachments. + * @param {String} [siteId] Site ID. If not defined, current site. + * @param {Number} [userId] User the entry belong to. If not defined, current user in site. + * @return {Promise} Promise resolved if stored, rejected if failure. */ - self.saveAddEntry = function(glossaryId, concept, definition, courseId, options, siteId, userId) { + self.saveAddEntry = function(glossaryId, concept, definition, courseId, options, attach, siteId, userId) { return $mmSitesManager.getSite(siteId).then(function(site) { userId = userId || site.getUserId(); - var now = new Date().getTime(), - entry = { + var entry = { glossaryid: glossaryId, courseid: courseId, concept: concept, @@ -149,13 +149,53 @@ angular.module('mm.addons.mod_glossary') definitionformat: 'html', options: options, userid: userId, - timecreated: now, - timemodified: now + timecreated: new Date().getTime() }; + if (attach) { + entry.attachments = attach; + } + return site.getDb().insert(mmaModGlossaryAddEntryStore, entry); }); }; + /** + * Get the path to the folder where to store files for offline attachments in a glossary. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryOffline#getGlossaryFolder + * @param {Number} glossaryId Glossary ID. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with the path. + */ + self.getGlossaryFolder = function(glossaryId, siteId) { + return $mmSitesManager.getSite(siteId).then(function(site) { + + var siteFolderPath = $mmFS.getSiteFolder(site.getId()), + folderPath = 'offlineglossary/' + glossaryId; + + return $mmFS.concatenatePaths(siteFolderPath, folderPath); + }); + }; + + /** + * Get the path to the folder where to store files for a new offline entry. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryOffline#getEntryFolder + * @param {Number} glossaryId Glossary ID. + * @param {Number} entryName The name of the entry. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with the path. + */ + self.getEntryFolder = function(glossaryId, entryName, siteId) { + return self.getGlossaryFolder(glossaryId, siteId).then(function(folderPath) { + return $mmFS.concatenatePaths(folderPath, 'newentry_' + entryName); + }); + }; + return self; }); diff --git a/www/addons/mod/glossary/services/glossary_sync.js b/www/addons/mod/glossary/services/glossary_sync.js index a95c5410bca..c098d41dacc 100644 --- a/www/addons/mod/glossary/services/glossary_sync.js +++ b/www/addons/mod/glossary/services/glossary_sync.js @@ -23,7 +23,7 @@ angular.module('mm.addons.mod_glossary') */ .factory('$mmaModGlossarySync', function($q, $log, $mmApp, $mmSitesManager, $mmaModGlossaryOffline, $mmSite, $mmEvents, $mmSync, $mmLang, mmaModGlossaryComponent, $mmaModGlossary, $translate, mmaModGlossaryAutomSyncedEvent, mmaModGlossarySyncTime, - $mmCourse, $mmSyncBlock, $mmUtil) { + $mmCourse, $mmSyncBlock, $mmUtil, $mmaModGlossaryHelper, $mmFileUploader) { $log = $log.getInstance('$mmaModGlossarySync'); @@ -174,18 +174,21 @@ angular.module('mm.addons.mod_glossary') courseId = data.courseid; - // A user has added some entries. - promise = $mmaModGlossary.addEntryOnline(glossaryId, data.concept, data.definition, data.options, siteId); + // First of all upload the attachments (if any). + promise = uploadAttachments(glossaryId, data, siteId).then(function(itemId) { + // Now try to add the entry. + return $mmaModGlossary.addEntryOnline(glossaryId, data.concept, data.definition, data.options, itemId, siteId); + }); promises.push(promise.then(function() { result.updated = true; - return $mmaModGlossaryOffline.deleteAddEntry(glossaryId, data.concept, siteId); + return deleteAddEntry(glossaryId, data.concept, siteId); }).catch(function(error) { if (error && error.wserror) { // The WebService has thrown an error, this means that responses cannot be submitted. Delete them. result.updated = true; - return $mmaModGlossaryOffline.deleteAddEntry(glossaryId, data.concept, siteId).then(function() { + return deleteAddEntry(glossaryId, data.concept, siteId).then(function() { // Responses deleted, add a warning. result.warnings.push($translate.instant('mm.core.warningofflinedatadeleted', { component: $mmCourse.translateModuleName('glossary'), @@ -223,6 +226,60 @@ angular.module('mm.addons.mod_glossary') return self.addOngoingSync(syncId, syncPromise, siteId); }; + /** + * Delete a new entry. + * + * @param {Number} glossaryId Glossary ID. + * @param {String} concept Glossary entry concept. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when deleted. + */ + function deleteAddEntry(glossaryId, concept, siteId) { + var promises = []; + + promises.push($mmaModGlossaryOffline.deleteAddEntry(glossaryId, concept, siteId)); + promises.push($mmaModGlossaryHelper.deleteStoredFiles(glossaryId, concept, siteId).catch(function() { + // Ignore errors, maybe there are no files. + })); + + return $q.all(promises); + } + + /** + * Upload attachments of an offline entry. + * + * @param {Number} glossaryId Glossary ID. + * @param {Object} entry Offline entry. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with draftid if uploaded, resolved with undefined if nothing to upload. + */ + function uploadAttachments(glossaryId, entry, siteId) { + var attachments = entry && entry.attachments; + if (attachments) { + // Has some attachments to sync. + var files = attachments.online || [], + promise; + + if (attachments.offline) { + // Has offline files. + promise = $mmaModGlossaryHelper.getStoredFiles(glossaryId, entry.concept, siteId).then(function(atts) { + files = files.concat(atts); + }).catch(function() { + // Folder not found, no files to add. + }); + } else { + promise = $q.when(); + } + + return promise.then(function() { + return $mmFileUploader.uploadOrReuploadFiles(files, mmaModGlossaryComponent, glossaryId, siteId); + }); + } + + // No attachments, resolve. + return $q.when(); + } + /** * Get the ID of a glossary sync. * diff --git a/www/addons/mod/glossary/services/helper.js b/www/addons/mod/glossary/services/helper.js new file mode 100644 index 00000000000..8285bdc2832 --- /dev/null +++ b/www/addons/mod/glossary/services/helper.js @@ -0,0 +1,150 @@ +// (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') + +/** + * Helper to gather some common functions for glossary. + * + * @module mm.addons.mod_glossary + * @ngdoc service + * @name $mmaModGlossaryHelper + */ +.factory('$mmaModGlossaryHelper', function($mmaModGlossaryOffline, $mmSite, $mmFileUploader, $mmFS, mmaModGlossaryComponent) { + + var self = {}; + + /** + * Clear temporary attachments because a new discussion or post was cancelled. + * Attachments already saved in an offline discussion or post will NOT be deleted. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryHelper#clearTmpFiles + * @param {Object[]} files List of current files. + * @return {Void} + */ + self.clearTmpFiles = function(files) { + // Delete the local files from the tmp folder. + files.forEach(function(file) { + if (!file.offline && file.remove) { + file.remove(); + } + }); + }; + + /** + * Delete stored attachment files for a new discussion. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryHelper#deleteStoredFiles + * @param {Number} glossaryId Glossary ID. + * @param {String} entryName The name of the entry. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when deleted. + */ + self.deleteStoredFiles = function(glossaryId, entryName, siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmaModGlossaryOffline.getEntryFolder(glossaryId, entryName, siteId).then(function(folderPath) { + return $mmFS.removeDir(folderPath); + }); + }; + + /** + * Get a list of stored attachment files for a new entry. See $mmaModGlossaryHelper#storeFiles. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryHelper#getStoredFiles + * @param {Number} glossaryId Glossary ID. + * @param {String} entryName The name of the entry. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with the files. + */ + self.getStoredFiles = function(glossaryId, entryName, siteId) { + siteId = siteId || $mmSite.getId(); + + return $mmaModGlossaryOffline.getEntryFolder(glossaryId, entryName, siteId).then(function(folderPath) { + return $mmFS.getDirectoryContents(folderPath).then(function(files) { + // Mark the files as pending offline. + angular.forEach(files, function(file) { + file.offline = true; + file.filename = file.name; + }); + return files; + }); + }); + }; + + /** + * Check if the data of an entry has changed. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryHelper#hasEntryDataChanged + * @param {Object} entry Current data. + * @param {Object} files Files attached. + * @return {Boolean} True if data has changed, false otherwise. + */ + self.hasEntryDataChanged = function(entry, files) { + return entry.text || entry.concept || files.length > 0; + }; + + /** + * Given a list of files (either online files or local files), store the local files in a local folder + * to be submitted later. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryHelper#storeFiles + * @param {Number} glossaryId Glossary ID. + * @param {String} entryName The name of the entry. + * @param {Object[]} files List of files. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved if success, rejected otherwise. + */ + self.storeFiles = function(glossaryId, entryName, files, siteId) { + siteId = siteId || $mmSite.getId(); + + // Get the folder where to store the files. + return $mmaModGlossaryOffline.getEntryFolder(glossaryId, entryName, siteId).then(function(folderPath) { + return $mmFileUploader.storeFilesToUpload(folderPath, files); + }); + }; + + /** + * Upload or store some files, depending if the user is offline or not. + * + * @module mm.addons.mod_glossary + * @ngdoc method + * @name $mmaModGlossaryHelper#uploadOrStoreFiles + * @param {Number} glossaryId Glossary ID. + * @param {String} entryName The name of the entry. + * @param {Object[]} files List of files. + * @param {Boolean} offline True if files sould be stored for offline, false to upload them. + * @param {String} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved if success. + */ + self.uploadOrStoreFiles = function(glossaryId, entryName, files, offline, siteId) { + if (offline) { + return self.storeFiles(glossaryId, entryName, files, siteId); + } else { + return $mmFileUploader.uploadOrReuploadFiles(files, mmaModGlossaryComponent, glossaryId, siteId); + } + }; + + return self; +}); \ No newline at end of file diff --git a/www/addons/mod/glossary/templates/edit.html b/www/addons/mod/glossary/templates/edit.html index 3132e9a8ecd..e9c6f859470 100644 --- a/www/addons/mod/glossary/templates/edit.html +++ b/www/addons/mod/glossary/templates/edit.html @@ -19,6 +19,8 @@ {{ 'mma.mod_glossary.aliases' | translate }}
+
{{ 'mma.mod_glossary.attachment' | translate }}
+
{{ 'mma.mod_glossary.linking' | translate }}