Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions gulpfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ function treatMergedData(data) {

addProperties(merged, data[filepath], 'mm.core.country-');

} else if (filepath.indexOf('core/assets/mimetypes') === 0) {

addProperties(merged, data[filepath], 'mm.core.mimetype-');

}
}

Expand Down Expand Up @@ -352,6 +356,7 @@ var paths = {
'./www/core/components/**/lang/',
'./www/addons/**/lang/',
'./www/core/assets/countries/',
'./www/core/assets/mimetypes/',
'!./www/**/' + remoteAddonPackageFolder + '/*.json',
'!./www/**/' + remoteAddonPackageFolder + '/**/*.json',
],
Expand Down
4 changes: 4 additions & 0 deletions upgrade.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
This files describes API changes in the Moodle Mobile app,
information provided here is intended especially for developers.

=== 3.3.2 ===

* Handlers registered in $mmFileUploaderDelegate now need to implement a function getSupportedMimeTypes. This function will receive a list of mimetypes and needs to return the ones supported by the handler. Also, the handler's action and afterRender functions now receive a new parameter: mimetypes.

=== 3.3 ===

* The project now supports Ionic CLI v2 and Node 6.9. We recommend updating node, npm, Ionic CLI and project dependencies:
Expand Down
80 changes: 79 additions & 1 deletion www/addons/mod/assign/submission/file/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,81 @@ angular.module('mm.addons.mod_assign')
* @name mmaModAssignSubmissionFile
*/
.directive('mmaModAssignSubmissionFile', function($mmaModAssign, $mmFileSession, mmaModAssignComponent, $mmaModAssignHelper,
$mmaModAssignOffline, mmaModAssignSubmissionFileName, $mmFileUploaderHelper, $q) {
$mmaModAssignOffline, mmaModAssignSubmissionFileName, $mmFileUploaderHelper, $q, $mmFS) {

/**
* Add a dot to the beginning of an extension.
*
* @param {String} extension Extension.
* @return {String} Treated extension.
*/
function addDot(extension) {
return '.' + extension;
}

/**
* Parse filetypeslist to get the list of allowed mimetypes and the data to render information.
*
* @param {Object} scope Directive's scope.
* @return {Void}
*/
function treatFileTypes(scope) {
var mimetypes = {}, // Use an object to prevent duplicates.
filetypes = scope.configs.filetypeslist.replace(/,/g, ';').split(';');

scope.typesInfo = [];

angular.forEach(filetypes, function(filetype) {
filetype = filetype.trim();

if (filetype) {
if (filetype.indexOf('/') != -1) {
// It's a mimetype.
mimetypes[filetype] = true;

scope.typesInfo.push({
type: 'mimetype',
value: {
name: $mmFS.getMimetypeDescription(filetype),
extlist: $mmFS.getExtensions(filetype).map(addDot).join(' ')
}
});
} else if (filetype.indexOf('.') === 0) {
// It's an extension.
var mimetype = $mmFS.getMimeType(filetype);
if (mimetype) {
mimetypes[mimetype] = true;
}

scope.typesInfo.push({
type: 'extension',
value: filetype
});
} else {
// It's a group.
var groupMimetypes = $mmFS.getGroupMimeInfo(filetype, 'mimetypes'),
groupExtensions = $mmFS.getGroupMimeInfo(filetype, 'extensions');

angular.forEach(groupMimetypes, function(mimetype) {
if (mimetype) {
mimetypes[mimetype] = true;
}
});

scope.typesInfo.push({
type: 'mimetype',
value: {
name: $mmFS.getTranslatedGroupName(filetype),
extlist: groupExtensions ? groupExtensions.map(addDot).join(' ') : ''
}
});
}
}
});

scope.mimetypes = Object.keys(mimetypes);
}

return {
restrict: 'A',
priority: 100,
Expand All @@ -32,6 +106,10 @@ angular.module('mm.addons.mod_assign')
return;
}

if (scope.edit && scope.configs && scope.configs.filetypeslist && scope.configs.filetypeslist.trim()) {
treatFileTypes(scope);
}

// Get the offline data.
$mmaModAssignOffline.getSubmission(scope.assign.id).catch(function() {
// Error getting data, assume there's no offline submission.
Expand Down
2 changes: 2 additions & 0 deletions www/addons/mod/assign/submission/file/lang/en.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{
"filesofthesetypes": "Files of these types may be added to the submission:",
"filetypewithexts": "{{$a.name}} — {{$a.extlist}}",
"pluginname": "File submissions"
}
11 changes: 10 additions & 1 deletion www/addons/mod/assign/submission/file/template.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
<!-- Edit -->
<div ng-if="edit">
<h2 class="item item-text-wrap item-divider">{{plugin.name}}</h2>
<mm-attachments files="files" max-size="{{configs.maxsubmissionsizebytes}}" max-submissions="{{configs.maxfilesubmissions}}" component="{{assignComponent}}" component-id="{{assign.cmid}}" allow-offline="{{allowOffline}}"></mm-attachments>
<mm-attachments files="files" max-size="{{configs.maxsubmissionsizebytes}}" max-submissions="{{configs.maxfilesubmissions}}" component="{{assignComponent}}" mimetypes="mimetypes" component-id="{{assign.cmid}}" allow-offline="{{allowOffline}}"></mm-attachments>
<div class="item item-text-wrap" ng-if="mimetypes && mimetypes.length">
<p>{{ 'mma.mod_assign_submission_file.filesofthesetypes' | translate }}</p>
<ul class="list-with-style">
<li ng-repeat="typeInfo in typesInfo">
<p ng-if="typeInfo.type == 'extension'">{{typeInfo.value}}</p>
<p ng-if="typeInfo.type != 'extension'" ng-bind-html="'mma.mod_assign_submission_file.filetypewithexts' | translate:{$a: typeInfo.value}"></p>
</li>
</ul>
</div>
</div>

<div class="item item-text-wrap" ng-if="files.length && !edit">
Expand Down
54 changes: 54 additions & 0 deletions www/core/assets/mimetypes/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
{
"application/epub_zip": "EPUB ebook",
"application/msword": "Word document",
"application/pdf": "PDF document",
"application/vnd.moodle.backup": "Moodle backup",
"application/vnd.ms-excel": "Excel spreadsheet",
"application/vnd.ms-excel.sheet.macroEnabled.12": "Excel 2007 macro-enabled workbook",
"application/vnd.ms-powerpoint": "Powerpoint presentation",
"application/vnd.oasis.opendocument.spreadsheet": "OpenDocument Spreadsheet",
"application/vnd.oasis.opendocument.spreadsheet-template": "OpenDocument Spreadsheet template",
"application/vnd.oasis.opendocument.text": "OpenDocument Text document",
"application/vnd.oasis.opendocument.text-template": "OpenDocument Text template",
"application/vnd.oasis.opendocument.text-web": "OpenDocument Web page template",
"application/vnd.openxmlformats-officedocument.presentationml.presentation": "Powerpoint 2007 presentation",
"application/vnd.openxmlformats-officedocument.presentationml.slideshow": "Powerpoint 2007 slideshow",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "Excel 2007 spreadsheet",
"application/vnd.openxmlformats-officedocument.spreadsheetml.template": "Excel 2007 template",
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": "Word 2007 document",
"application/x-iwork-keynote-sffkey": "iWork Keynote presentation",
"application/x-iwork-numbers-sffnumbers": "iWork Numbers spreadsheet",
"application/x-iwork-pages-sffpages": "iWork Pages document",
"application/x-javascript": "JavaScript source",
"application/x-mspublisher": "Publisher document",
"application/x-shockwave-flash": "Flash animation",
"application/xhtml_xml": "XHTML document",
"archive": "Archive ({{$a.EXT}})",
"audio": "Audio file ({{$a.EXT}})",
"default": "{{$a.mimetype}}",
"document/unknown": "File",
"group:archive": "Archive files",
"group:audio": "Audio files",
"group:document": "Document files",
"group:html_audio": "Audio files natively supported by browsers",
"group:html_track": "HTML track files",
"group:html_video": "Video files natively supported by browsers",
"group:image": "Image files",
"group:presentation": "Presentation files",
"group:sourcecode": "Source code",
"group:spreadsheet": "Spreadsheet files",
"group:video": "Video files",
"group:web_audio": "Audio files used on the web",
"group:web_file": "Web files",
"group:web_image": "Image files used on the web",
"group:web_video": "Video files used on the web",
"image": "Image ({{$a.MIMETYPE2}})",
"image/vnd.microsoft.icon": "Windows icon",
"text/css": "Cascading Style-Sheet",
"text/csv": "Comma-separated values",
"text/html": "HTML document",
"text/plain": "Text file",
"text/rtf": "RTF document",
"text/vtt": "Web Video Text Track",
"video": "Video file ({{$a.EXT}})"
}
54 changes: 49 additions & 5 deletions www/core/components/emulator/services/mediacapture.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ angular.module('mm.core.emulator')
extension,
quality = 0.92, // Image only.
returnData = false, // Image only.
isCaptureImage = false; // To identify if it's capturing an image using media capture plugin (instead of camera).
isCaptureImage = false, // To identify if it's capturing an image using media capture plugin (instead of camera).
mimeAndExt;

loadingModal = $mmUtil.showModalLoading();

Expand All @@ -72,13 +73,17 @@ angular.module('mm.core.emulator')
if (type == 'video') {
scope.isVideo = true;
title = 'mm.core.capturevideo';
mimetype = videoMimeType;
extension = possibleVideoMimeTypes[mimetype];

mimeAndExt = getMimeTypeAndExtension(type, options.mimetypes);
mimetype = mimeAndExt.mimetype;
extension = mimeAndExt.extension;
} else if (type == 'audio') {
scope.isAudio = true;
title = 'mm.core.captureaudio';
mimetype = audioMimeType;
extension = possibleAudioMimeTypes[mimetype];

mimeAndExt = getMimeTypeAndExtension(type, options.mimetypes);
mimetype = mimeAndExt.mimetype;
extension = mimeAndExt.extension;
} else if (type == 'image') {
scope.isImage = true;
title = 'mm.core.captureimage';
Expand Down Expand Up @@ -338,6 +343,45 @@ angular.module('mm.core.emulator')
}
}

/**
* Get the mimetype and extension to capture media.
*
* @param {String} type Type of media: image, audio, video.
* @param {String[]} [mimetypes] List of supported mimetypes. If undefined, all mimetypes supported.
* @return {Object} An object with mimetype and extension to use.
*/
function getMimeTypeAndExtension(type, mimetypes) {
var result = {};

if (mimetypes && mimetypes.length) {
// Search for a supported mimetype.
for (var i = 0; i < mimetypes.length; i++) {
var mimetype = mimetypes[i],
matches = mimetype.match(new RegExp('^' + type + '/'));

if (matches && matches.length && MediaRecorder.isTypeSupported(mimetype)) {
result.mimetype = mimetype;
break;
}
}
}

if (result.mimetype) {
// Found a supported mimetype in the mimetypes array, get the extension.
result.extension = $mmFS.getExtension(result.mimetype);
} else if (type == 'video') {
// No mimetype found, use default extension.
result.mimetype = videoMimeType;
result.extension = possibleVideoMimeTypes[result.mimetype];
} else if (type == 'audio') {
// No mimetype found, use default extension.
result.mimetype = audioMimeType;
result.extension = possibleAudioMimeTypes[result.mimetype];
}

return result;
}

/**
* Initialize the audio drawer. This code has been extracted from MDN's example on MediaStream Recording:
* https://github.com/mdn/web-dictaphone
Expand Down
1 change: 1 addition & 0 deletions www/core/components/fileuploader/lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
"errorwhileuploading": "An error occurred during the file upload.",
"file": "File",
"fileuploaded": "The file was successfully uploaded.",
"invalidfiletype": "{{$a}} filetype cannot be accepted.",
"maxbytesfile": "The file {{$a.file}} is too large. The maximum size you can upload is {{$a.size}}.",
"photoalbums": "Photo albums",
"readingfile": "Reading file",
Expand Down
32 changes: 26 additions & 6 deletions www/core/components/fileuploader/services/delegate.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,16 +36,18 @@ angular.module('mm.core.fileuploader')
* returning an object defining these functions. See {@link $mmUtil#resolveObject}.
* - isEnabled (Boolean|Promise) Whether or not the handler is enabled on a site level.
* When using a promise, it should return a boolean.
* - getSupportedMimeTypes(mimetypes) (String[]) Given a list of mimetypes, return the ones
* that are supported by the handler.
* - getData (Object) Returns an object with the data to display the handler. Accepted properties:
* * name Required. A name to identify the handler. Allows filtering it.
* * class Optional. Class to add to the handler's row.
* * title Required. Title to show in the handler's row.
* * icon Optional. Icon to show in the handler's row.
* * afterRender(maxSize, upload, allowOffline) Optional. Called when the handler is
* rendered.
* * action(maxSize, upload, allowOffline) Required. A function called when the handler
* is clicked. It must return an object - or a promise resolved with an object -
* containing these properties:
* * afterRender(maxSize, upload, allowOffline, mimetypes) Optional. Called when the
* handler is rendered.
* * action(maxSize, upload, allowOffline, mimetypes) Required. A function called when
* the handler is clicked. It must return an object - or a promise resolved with an
* object - containing these properties:
* - uploaded Boolean. Whether the handler uploaded or treated the file.
* - path String. Ignored if uploaded=true. The path of the file to upload.
* - fileEntry Object. Ignored if uploaded=true. The fileEntry to upload.
Expand Down Expand Up @@ -93,14 +95,32 @@ angular.module('mm.core.fileuploader')
* @module mm.core.fileuploader
* @ngdoc method
* @name $mmFileUploaderDelegate#getHandlers
* @param {String[]} [mimetypes] List of supported mimetypes. If undefined, all mimetypes supported.
* @return {Promise} Resolved with an array of objects containing 'priority' and the handler data.
*/
self.getHandlers = function() {
self.getHandlers = function(mimetypes) {
var handlers = [];

angular.forEach(enabledHandlers, function(handler) {
var supportedMimetypes;

if (mimetypes) {
if (!handler.instance.getSupportedMimeTypes) {
// Handler doesn't implement a required function, don't add it.
return;
}

supportedMimetypes = handler.instance.getSupportedMimeTypes(mimetypes);

if (!supportedMimetypes.length) {
// Handler doesn't support any mimetype, don't add it.
return;
}
}

var data = handler.instance.getData();
data.priority = handler.priority;
data.mimetypes = supportedMimetypes;
handlers.push(data);
});

Expand Down
Loading