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 @@
+