MOBILE-2267 forum: Edit forum post

main
Pau Ferrer Ocaña 2019-11-01 09:44:48 +01:00
parent f1a86b2a36
commit 470abc056b
15 changed files with 366 additions and 28 deletions

View File

@ -389,7 +389,6 @@
"addon.mod_assign.numwords": "moodle", "addon.mod_assign.numwords": "moodle",
"addon.mod_assign.outof": "assign", "addon.mod_assign.outof": "assign",
"addon.mod_assign.overdue": "assign", "addon.mod_assign.overdue": "assign",
"addon.mod_assign.savechanges": "assign",
"addon.mod_assign.submission": "assign", "addon.mod_assign.submission": "assign",
"addon.mod_assign.submissioneditable": "assign", "addon.mod_assign.submissioneditable": "assign",
"addon.mod_assign.submissionnoteditable": "assign", "addon.mod_assign.submissionnoteditable": "assign",
@ -572,6 +571,7 @@
"addon.mod_forum.cannotadddiscussionall": "forum", "addon.mod_forum.cannotadddiscussionall": "forum",
"addon.mod_forum.cannotcreatediscussion": "forum", "addon.mod_forum.cannotcreatediscussion": "forum",
"addon.mod_forum.couldnotadd": "forum", "addon.mod_forum.couldnotadd": "forum",
"addon.mod_forum.couldnotupdate": "forum",
"addon.mod_forum.cutoffdatereached": "forum", "addon.mod_forum.cutoffdatereached": "forum",
"addon.mod_forum.delete": "forum", "addon.mod_forum.delete": "forum",
"addon.mod_forum.deletedpost": "forum", "addon.mod_forum.deletedpost": "forum",
@ -625,6 +625,7 @@
"addon.mod_forum.unpindiscussion": "forum", "addon.mod_forum.unpindiscussion": "forum",
"addon.mod_forum.unread": "forum", "addon.mod_forum.unread": "forum",
"addon.mod_forum.unreadpostsnumber": "forum", "addon.mod_forum.unreadpostsnumber": "forum",
"addon.mod_forum.yourreply": "forum",
"addon.mod_glossary.addentry": "glossary", "addon.mod_glossary.addentry": "glossary",
"addon.mod_glossary.aliases": "glossary", "addon.mod_glossary.aliases": "glossary",
"addon.mod_glossary.attachment": "glossary", "addon.mod_glossary.attachment": "glossary",
@ -1792,6 +1793,7 @@
"core.restricted": "moodle", "core.restricted": "moodle",
"core.retry": "local_moodlemobileapp", "core.retry": "local_moodlemobileapp",
"core.save": "moodle", "core.save": "moodle",
"core.savechanges": "assign",
"core.search": "moodle", "core.search": "moodle",
"core.searching": "local_moodlemobileapp", "core.searching": "local_moodlemobileapp",
"core.searchresults": "moodle", "core.searchresults": "moodle",

View File

@ -71,7 +71,6 @@
"numwords": "{{$a}} words", "numwords": "{{$a}} words",
"outof": "{{$a.current}} out of {{$a.total}}", "outof": "{{$a.current}} out of {{$a.total}}",
"overdue": "<font color=\"red\">Assignment is overdue by: {{$a}}</font>", "overdue": "<font color=\"red\">Assignment is overdue by: {{$a}}</font>",
"savechanges": "Save changes",
"submissioneditable": "Student can edit this submission", "submissioneditable": "Student can edit this submission",
"submissionnoteditable": "Student cannot edit this submission", "submissionnoteditable": "Student cannot edit this submission",
"submissionnotsupported": "This submission is not supported by the app and may not contain all the information.", "submissionnotsupported": "This submission is not supported by the app and may not contain all the information.",

View File

@ -51,10 +51,12 @@
</div> </div>
<div class="addon-mod-forum-discussion-info"> <div class="addon-mod-forum-discussion-info">
<ion-avatar core-user-avatar [user]="discussion" item-start [courseId]="courseId" *ngIf="discussion.userfullname"></ion-avatar> <ion-avatar core-user-avatar [user]="discussion" item-start [courseId]="courseId" *ngIf="discussion.userfullname"></ion-avatar>
<div class="addon-mod-forum-discussion-author">
<h3 *ngIf="discussion.userfullname">{{discussion.userfullname}}</h3> <h3 *ngIf="discussion.userfullname">{{discussion.userfullname}}</h3>
<p *ngIf="discussion.groupname"><ion-icon name="people"></ion-icon> {{ discussion.groupname }}</p> <p *ngIf="discussion.groupname"><ion-icon name="people"></ion-icon> {{ discussion.groupname }}</p>
<p><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p> <p><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p>
</div> </div>
</div>
</ion-item> </ion-item>
</ng-container> </ng-container>
<ng-container *ngFor="let discussion of discussions"> <ng-container *ngFor="let discussion of discussions">

View File

@ -0,0 +1,11 @@
addon-forum-post-options-menu {
core-loading:not(.core-loading-loaded) > .core-loading-container {
position: relative !important;
padding-top: 10px !important;
padding-bottom: 10px !important;
overflow: hidden;
}
core-loading > .core-loading-container .core-loading-message {
display: none;
}
}

View File

@ -46,9 +46,9 @@ export class AddonForumPostOptionsMenuComponent implements OnInit {
ngOnInit(): void { ngOnInit(): void {
if (this.forumId) { if (this.forumId) {
if (this.post.id) { if (this.post.id) {
this.forumProvider.getDiscussionPost(this.forumId, this.post.discussion, this.post.id).then((post) => { this.forumProvider.getDiscussionPost(this.forumId, this.post.discussion, this.post.id, true).then((post) => {
this.canDelete = post.capabilities.delete && this.forumProvider.isDeletePostAvailable(); this.canDelete = post.capabilities.delete && this.forumProvider.isDeletePostAvailable();
this.canEdit = false; this.canEdit = post.capabilities.edit && this.forumProvider.isUpdatePostAvailable();
this.wordCount = post.wordcount; this.wordCount = post.wordcount;
}).catch((error) => { }).catch((error) => {
this.domUtils.showErrorModalDefault(error, 'Error getting discussion post.'); this.domUtils.showErrorModalDefault(error, 'Error getting discussion post.');

View File

@ -7,6 +7,9 @@
<core-icon name="fa-star" class="addon-forum-star" *ngIf="post.parent == 0 && !post.pinned && post.starred"></core-icon> <core-icon name="fa-star" class="addon-forum-star" *ngIf="post.parent == 0 && !post.pinned && post.starred"></core-icon>
<core-format-text [text]="post.subject" contextLevel="module" [contextInstanceId]="forum && forum.cmid" [courseId]="courseId"></core-format-text> <core-format-text [text]="post.subject" contextLevel="module" [contextInstanceId]="forum && forum.cmid" [courseId]="courseId"></core-format-text>
</h2> </h2>
<ion-note float-end padding-left text-end *ngIf="trackPosts && !post.postread" [attr.aria-label]="'addon.mod_forum.unread' | translate">
<core-icon name="fa-circle" color="primary"></core-icon>
</ion-note>
<button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="optionsMenuEnabled"> <button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="optionsMenuEnabled">
<core-icon name="more"></core-icon> <core-icon name="more"></core-icon>
</button> </button>
@ -19,12 +22,14 @@
<p *ngIf="post.modified">{{post.modified * 1000 | coreFormatDate: "strftimerecentfull"}}</p> <p *ngIf="post.modified">{{post.modified * 1000 | coreFormatDate: "strftimerecentfull"}}</p>
<p *ngIf="!post.modified"><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p> <p *ngIf="!post.modified"><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</p>
</div> </div>
<ng-container *ngIf="!displaySubject">
<ion-note float-end padding-left text-end *ngIf="trackPosts && !post.postread" [attr.aria-label]="'addon.mod_forum.unread' | translate"> <ion-note float-end padding-left text-end *ngIf="trackPosts && !post.postread" [attr.aria-label]="'addon.mod_forum.unread' | translate">
<core-icon name="fa-circle" color="primary"></core-icon> <core-icon name="fa-circle" color="primary"></core-icon>
</ion-note> </ion-note>
<button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="!displaySubject && optionsMenuEnabled"> <button ion-button icon-only clear color="dark" (click)="showOptionsMenu($event)" *ngIf="optionsMenuEnabled">
<core-icon name="more"></core-icon> <core-icon name="more"></core-icon>
</button> </button>
</ng-container>
</div> </div>
</ion-item> </ion-item>
</ion-card-header> </ion-card-header>

View File

@ -14,7 +14,7 @@
import { Component, Input, Output, Optional, EventEmitter, OnInit, OnDestroy } from '@angular/core'; import { Component, Input, Output, Optional, EventEmitter, OnInit, OnDestroy } from '@angular/core';
import { FormControl } from '@angular/forms'; import { FormControl } from '@angular/forms';
import { Content, PopoverController } from 'ionic-angular'; import { Content, PopoverController, ModalController } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader'; import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
import { CoreSyncProvider } from '@providers/sync'; import { CoreSyncProvider } from '@providers/sync';
@ -75,6 +75,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
private tagProvider: CoreTagProvider, private tagProvider: CoreTagProvider,
@Optional() private content: Content, @Optional() private content: Content,
protected popoverCtrl: PopoverController, protected popoverCtrl: PopoverController,
protected modalCtrl: ModalController,
protected eventsProvider: CoreEventsProvider, protected eventsProvider: CoreEventsProvider,
protected sitesProvider: CoreSitesProvider) { protected sitesProvider: CoreSitesProvider) {
this.onPostChange = new EventEmitter<void>(); this.onPostChange = new EventEmitter<void>();
@ -93,7 +94,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
this.post.subject != reTranslated + this.defaultSubject); this.post.subject != reTranslated + this.defaultSubject);
this.optionsMenuEnabled = !this.post.id || (this.forumProvider.isGetDiscussionPostAvailable() && this.optionsMenuEnabled = !this.post.id || (this.forumProvider.isGetDiscussionPostAvailable() &&
(this.forumProvider.isDeletePostAvailable())); (this.forumProvider.isDeletePostAvailable() || this.forumProvider.isUpdatePostAvailable()));
} }
/** /**
@ -179,7 +180,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
if (data && data.action) { if (data && data.action) {
switch (data.action) { switch (data.action) {
case 'edit': case 'edit':
// Not implemented. this.editPost();
break; break;
case 'editoffline': case 'editoffline':
this.editOfflineReply(); this.editOfflineReply();
@ -200,6 +201,61 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
}); });
} }
/**
* Shows a form modal to edit an online post.
*/
editPost(): void {
const modal = this.modalCtrl.create('AddonModForumEditPostPage', {
post: this.post,
component: this.component,
componentId: this.componentId,
forum: this.forum
});
modal.present();
modal.onDidDismiss((data) => {
if (typeof data != 'undefined') {
// Add some HTML to the message if needed.
const message = this.textUtils.formatHtmlLines(data.message);
const files = data.files || [];
const sendingModal = this.domUtils.showModalLoading('core.sending', true);
let promise;
// Upload attachments first if any.
if (files.length) {
promise = this.forumHelper.uploadOrStoreReplyFiles(this.forum.id, this.post.id, files, false);
} else {
promise = Promise.resolve();
}
promise.then((attach) => {
const options: any = {};
if (attach) {
options.attachmentsid = attach;
}
// Try to send it to server.
return this.forumProvider.updatePost(this.post.id, data.subject, message, options);
}).then((sent) => {
if (sent && this.forum.id) {
// Data sent to server, delete stored files (if any).
this.forumHelper.deleteReplyStoredFiles(this.forum.id, this.post.id);
this.onPostChange.emit();
this.post.subject = data.subject;
this.post.message = message;
this.post.attachments = data.files;
}
}).catch((message) => {
this.domUtils.showErrorModalDefault(message, 'addon.mod_forum.couldnotupdate', true);
}).finally(() => {
sendingModal.dismiss();
});
}
});
}
/** /**
* Set this post as being replied to. * Set this post as being replied to.
*/ */

View File

@ -8,6 +8,7 @@
"cannotadddiscussionall": "You do not have permission to add a new discussion topic for all participants.", "cannotadddiscussionall": "You do not have permission to add a new discussion topic for all participants.",
"cannotcreatediscussion": "Could not create new discussion", "cannotcreatediscussion": "Could not create new discussion",
"couldnotadd": "Could not add your post due to an unknown error", "couldnotadd": "Could not add your post due to an unknown error",
"couldnotupdate": "Could not update your post due to an unknown error",
"cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.", "cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.",
"delete": "Delete", "delete": "Delete",
"deletedpost": "The post has been deleted", "deletedpost": "The post has been deleted",
@ -60,5 +61,6 @@
"unlockdiscussion": "Unlock this discussion", "unlockdiscussion": "Unlock this discussion",
"unpindiscussion": "Unpin this discussion", "unpindiscussion": "Unpin this discussion",
"unread": "Unread", "unread": "Unread",
"unreadpostsnumber": "{{$a}} unread posts" "unreadpostsnumber": "{{$a}} unread posts",
"yourreply": "Your reply"
} }

View File

@ -0,0 +1,40 @@
<ion-header>
<ion-navbar core-back-button>
<ion-title>{{ 'addon.mod_forum.yourreply' | translate }}</ion-title>
<ion-buttons end>
<button ion-button icon-only (click)="closeModal()" [attr.aria-label]="'core.close' | translate">
<ion-icon name="close"></ion-icon>
</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content>
<ion-list>
<ion-item>
<ion-label stacked>{{ 'addon.mod_forum.subject' | translate }}</ion-label>
<ion-input type="text" [placeholder]="'addon.mod_forum.subject' | translate" [(ngModel)]="replyData.subject"></ion-input>
</ion-item>
<ion-item>
<ion-label stacked>{{ 'addon.mod_forum.message' | translate }}</ion-label>
<core-rich-text-editor item-content [control]="messageControl" (contentChanged)="onMessageChange($event)" [placeholder]="'addon.mod_forum.replyplaceholder' | translate" [name]="'mod_forum_reply_' + replyData.id" [component]="component" [componentId]="componentId"></core-rich-text-editor>
</ion-item>
<ion-item-divider text-wrap (click)="toggleAdvanced()" class="core-expandable">
<core-icon *ngIf="!advanced" name="fa-caret-right" item-start></core-icon>
<core-icon *ngIf="advanced" name="fa-caret-down" item-start></core-icon>
{{ 'addon.mod_forum.advanced' | translate }}
</ion-item-divider>
<ng-container *ngIf="advanced">
<core-attachments *ngIf="forum.id && forum.maxattachments > 0" [files]="replyData.files" [maxSize]="forum.maxbytes" [maxSubmissions]="forum.maxattachments" [component]="component" [componentId]="forum.cmid" [allowOffline]="true"></core-attachments>
</ng-container>
<ion-grid>
<ion-row>
<ion-col>
<button ion-button block (click)="reply($event)" [disabled]="replyData.subject == '' || replyData.message == null">{{ 'core.savechanges' | translate }}</button>
</ion-col>
<ion-col>
<button ion-button block color="light" (click)="closeModal()">{{ 'core.cancel' | translate }}</button>
</ion-col>
</ion-row>
</ion-grid>
</ion-list>
</ion-content>

View File

@ -0,0 +1,35 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// 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.
import { NgModule } from '@angular/core';
import { IonicPageModule } from 'ionic-angular';
import { TranslateModule } from '@ngx-translate/core';
import { CoreComponentsModule } from '@components/components.module';
import { CoreDirectivesModule } from '@directives/directives.module';
import { AddonModForumComponentsModule } from '../../components/components.module';
import { AddonModForumEditPostPage } from './edit-post';
@NgModule({
declarations: [
AddonModForumEditPostPage,
],
imports: [
CoreComponentsModule,
CoreDirectivesModule,
AddonModForumComponentsModule,
IonicPageModule.forChild(AddonModForumEditPostPage),
TranslateModule.forChild()
],
})
export class AddonModForumEditPostPageModule {}

View File

@ -0,0 +1,141 @@
// (C) Copyright 2015 Moodle Pty Ltd.
//
// 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.
import { Component } from '@angular/core';
import { FormControl } from '@angular/forms';
import { IonicPage, ViewController, NavParams } from 'ionic-angular';
import { TranslateService } from '@ngx-translate/core';
import { CoreFileUploaderProvider } from '@core/fileuploader/providers/fileuploader';
import { CoreDomUtilsProvider } from '@providers/utils/dom';
import { AddonModForumProvider } from '../../providers/forum';
import { AddonModForumHelperProvider } from '../../providers/helper';
/**
* Page that displays a form to edit discussion post.
*/
@IonicPage({ segment: 'addon-mod-edit-post' })
@Component({
selector: 'addon-mod-forum-edit-post',
templateUrl: 'addon-mod-forum-edit-post.html',
})
export class AddonModForumEditPostPage {
component: string; // Component this post belong to.
componentId: number; // Component ID.
forum: any; // The forum the post belongs to. Required for attachments and offline posts.
messageControl = new FormControl();
advanced = false; // Display all form fields.
replyData: any = {};
originalData: any = {}; // Object with the original post data. Usually shared between posts.
protected forceLeave = false; // To allow leaving the page without checking for changes.
constructor(
params: NavParams,
protected forumProvider: AddonModForumProvider,
protected viewCtrl: ViewController,
protected domUtils: CoreDomUtilsProvider,
protected uploaderProvider: CoreFileUploaderProvider,
protected forumHelper: AddonModForumHelperProvider,
protected translate: TranslateService) {
const post = params.get('post');
this.component = params.get('component');
this.componentId = params.get('componentId');
this.forum = params.get('forum');
this.replyData.id = post.id;
this.replyData.subject = post.subject;
this.replyData.message = post.message;
this.replyData.files = post.attachments || [];
// Delete the local files from the tmp folder if any.
this.uploaderProvider.clearTmpFiles(this.replyData.files);
// Update rich text editor.
this.messageControl.setValue(this.replyData.message);
// Update original data.
this.originalData.subject = this.replyData.subject;
this.originalData.message = this.replyData.message;
this.originalData.files = this.replyData.files.slice();
// Show advanced fields if any of them has not the default value.
this.advanced = this.replyData.files.length > 0;
}
/**
* Check if we can leave the page or not.
*
* @return Resolved if we can leave it, rejected if not.
*/
ionViewCanLeave(): boolean | Promise<void> {
if (this.forceLeave) {
return true;
}
let promise: any;
if (this.forumHelper.hasPostDataChanged(this.replyData, this.originalData)) {
// Show confirmation if some data has been modified.
promise = this.domUtils.showConfirm(this.translate.instant('core.confirmcanceledit'));
} else {
promise = Promise.resolve();
}
return promise.then(() => {
// Delete the local files from the tmp folder.
this.uploaderProvider.clearTmpFiles(this.replyData.files);
});
}
/**
* Message changed.
*
* @param text The new text.
*/
onMessageChange(text: string): void {
this.replyData.message = text;
}
/**
* Close modal.
*
* @param data Data to return to the page.
*/
closeModal(data: any): void {
this.viewCtrl.dismiss(data);
}
/**
* Reply to this post.
*
* @param e Click event.
*/
reply(e: Event): void {
e.preventDefault();
e.stopPropagation();
// Close the modal, sending the input data.
this.forceLeave = true;
this.closeModal(this.replyData);
}
/**
* Show or hide advanced form fields.
*/
toggleAdvanced(): void {
this.advanced = !this.advanced;
}
}

View File

@ -317,6 +317,16 @@ export class AddonModForumProvider {
return this.sitesProvider.wsAvailableInCurrentSite('mod_forum_delete_post'); return this.sitesProvider.wsAvailableInCurrentSite('mod_forum_delete_post');
} }
/**
* Returns whether or not updatePost WS available or not.
*
* @return If WS is avalaible.
* @since 3.8
*/
isUpdatePostAvailable(): boolean {
return this.sitesProvider.wsAvailableInCurrentSite('mod_forum_update_discussion_post');
}
/** /**
* Format discussions, setting groupname if the discussion group is valid. * Format discussions, setting groupname if the discussion group is valid.
* *
@ -385,19 +395,25 @@ export class AddonModForumProvider {
* @param forumId Forum ID. * @param forumId Forum ID.
* @param discussionId Discussion ID. * @param discussionId Discussion ID.
* @param postId Post ID. * @param postId Post ID.
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
* @param siteId Site ID. If not defined, current site. * @param siteId Site ID. If not defined, current site.
* @return Promise resolved when the post is retrieved. * @return Promise resolved when the post is retrieved.
*/ */
getDiscussionPost(forumId: number, discussionId: number, postId: number, siteId?: string): Promise<any> { getDiscussionPost(forumId: number, discussionId: number, postId: number, ignoreCache?: boolean, siteId?: string): Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => { return this.sitesProvider.getSite(siteId).then((site) => {
const params = { const params = {
postid: postId postid: postId
}; },
const preSets = { preSets: CoreSiteWSPreSets = {
cacheKey: this.getDiscussionPostDataCacheKey(forumId, discussionId, postId), cacheKey: this.getDiscussionPostDataCacheKey(forumId, discussionId, postId),
updateFrequency: CoreSite.FREQUENCY_RARELY updateFrequency: CoreSite.FREQUENCY_USUALLY
}; };
if (ignoreCache) {
preSets.getFromCache = false;
preSets.emergencyCache = false;
}
return site.read('mod_forum_get_discussion_post', params, preSets).then((response) => { return site.read('mod_forum_get_discussion_post', params, preSets).then((response) => {
if (response.post) { if (response.post) {
return response.post; return response.post;
@ -1050,4 +1066,29 @@ export class AddonModForumProvider {
this.userProvider.storeUsers(this.utils.objectToArray(users)); this.userProvider.storeUsers(this.utils.objectToArray(users));
} }
/**
* Update a certain post.
*
* @param postId ID of the post being edited.
* @param subject New post's subject.
* @param message New post's message.
* @param options Options (subscribe, attachments, ...).
* @param siteId Site ID. If not defined, current site.
* @return Promise resolved with success boolean when done.
*/
updatePost(postId: number, subject: string, message: string, options?: any, siteId?: string): Promise<boolean> {
return this.sitesProvider.getSite(siteId).then((site) => {
const params = {
postid: postId,
subject: subject,
message: message,
options: this.utils.objectToArrayOfObjects(options, 'name', 'value')
};
return site.write('mod_forum_update_discussion_post', params).then((response) => {
return response && response.status;
});
});
}
} }

View File

@ -388,7 +388,6 @@
"addon.mod_assign.numwords": "{{$a}} words", "addon.mod_assign.numwords": "{{$a}} words",
"addon.mod_assign.outof": "{{$a.current}} out of {{$a.total}}", "addon.mod_assign.outof": "{{$a.current}} out of {{$a.total}}",
"addon.mod_assign.overdue": "<font color=\"red\">Assignment is overdue by: {{$a}}</font>", "addon.mod_assign.overdue": "<font color=\"red\">Assignment is overdue by: {{$a}}</font>",
"addon.mod_assign.savechanges": "Save changes",
"addon.mod_assign.submission": "Submission", "addon.mod_assign.submission": "Submission",
"addon.mod_assign.submissioneditable": "Student can edit this submission", "addon.mod_assign.submissioneditable": "Student can edit this submission",
"addon.mod_assign.submissionnoteditable": "Student cannot edit this submission", "addon.mod_assign.submissionnoteditable": "Student cannot edit this submission",
@ -571,6 +570,7 @@
"addon.mod_forum.cannotadddiscussionall": "You do not have permission to add a new discussion topic for all participants.", "addon.mod_forum.cannotadddiscussionall": "You do not have permission to add a new discussion topic for all participants.",
"addon.mod_forum.cannotcreatediscussion": "Could not create new discussion", "addon.mod_forum.cannotcreatediscussion": "Could not create new discussion",
"addon.mod_forum.couldnotadd": "Could not add your post due to an unknown error", "addon.mod_forum.couldnotadd": "Could not add your post due to an unknown error",
"addon.mod_forum.couldnotupdate": "Could not update your post due to an unknown error",
"addon.mod_forum.cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.", "addon.mod_forum.cutoffdatereached": "The cut-off date for posting to this forum is reached so you can no longer post to it.",
"addon.mod_forum.delete": "Delete", "addon.mod_forum.delete": "Delete",
"addon.mod_forum.deletedpost": "The post has been deleted", "addon.mod_forum.deletedpost": "The post has been deleted",
@ -624,6 +624,7 @@
"addon.mod_forum.unpindiscussion": "Unpin this discussion", "addon.mod_forum.unpindiscussion": "Unpin this discussion",
"addon.mod_forum.unread": "Unread", "addon.mod_forum.unread": "Unread",
"addon.mod_forum.unreadpostsnumber": "{{$a}} unread posts", "addon.mod_forum.unreadpostsnumber": "{{$a}} unread posts",
"addon.mod_forum.yourreply": "Your reply",
"addon.mod_glossary.addentry": "Add a new entry", "addon.mod_glossary.addentry": "Add a new entry",
"addon.mod_glossary.aliases": "Keyword(s)", "addon.mod_glossary.aliases": "Keyword(s)",
"addon.mod_glossary.attachment": "Attachment", "addon.mod_glossary.attachment": "Attachment",
@ -1787,6 +1788,7 @@
"core.restricted": "Restricted", "core.restricted": "Restricted",
"core.retry": "Retry", "core.retry": "Retry",
"core.save": "Save", "core.save": "Save",
"core.savechanges": "Save changes",
"core.search": "Search", "core.search": "Search",
"core.searching": "Searching", "core.searching": "Searching",
"core.searchresults": "Search results", "core.searchresults": "Search results",

View File

@ -216,6 +216,7 @@
"restricted": "Restricted", "restricted": "Restricted",
"retry": "Retry", "retry": "Retry",
"save": "Save", "save": "Save",
"savechanges": "Save changes",
"search": "Search", "search": "Search",
"searching": "Searching", "searching": "Searching",
"searchresults": "Search results", "searchresults": "Search results",

View File

@ -146,24 +146,25 @@ export class CoreMimetypeUtilsProvider {
*/ */
getEmbeddedHtml(file: any, path?: string): string { getEmbeddedHtml(file: any, path?: string): string {
let ext; let ext;
const filename = file.filename || file.name;
if (file.mimetype) { if (file.mimetype) {
ext = this.getExtension(file.mimetype); ext = this.getExtension(file.mimetype);
} else { } else {
ext = this.getFileExtension(file.filename); ext = this.getFileExtension(filename);
file.mimetype = this.getMimeType(ext); file.mimetype = this.getMimeType(ext);
} }
if (this.canBeEmbedded(ext)) { if (this.canBeEmbedded(ext)) {
file.embedType = this.getExtensionType(ext); file.embedType = this.getExtensionType(ext);
path = path || file.fileurl; path = path || file.fileurl || (file.toURL && file.toURL());
if (file.embedType == 'image') { if (file.embedType == 'image') {
return '<img src="' + path + '">'; return '<img src="' + path + '">';
} }
if (file.embedType == 'audio' || file.embedType == 'video') { if (file.embedType == 'audio' || file.embedType == 'video') {
return '<' + file.embedType + ' controls title="' + file.filename + '" src="' + path + '">' + return '<' + file.embedType + ' controls title="' + filename + '" src="' + path + '">' +
'<source src="' + path + '" type="' + file.mimetype + '">' + '<source src="' + path + '" type="' + file.mimetype + '">' +
'</' + file.embedType + '>'; '</' + file.embedType + '>';
} }