commit
134e2d7203
|
@ -44,12 +44,6 @@ export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmiss
|
||||||
* Component being initialized.
|
* Component being initialized.
|
||||||
*/
|
*/
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
// Get the offline data.
|
|
||||||
const filesData = await CoreUtils.ignoreErrors(
|
|
||||||
AddonModAssignOffline.getSubmission(this.assign.id),
|
|
||||||
undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
this.acceptedTypes = this.configs?.filetypeslist;
|
this.acceptedTypes = this.configs?.filetypeslist;
|
||||||
this.maxSize = this.configs?.maxsubmissionsizebytes
|
this.maxSize = this.configs?.maxsubmissionsizebytes
|
||||||
? parseInt(this.configs.maxsubmissionsizebytes, 10)
|
? parseInt(this.configs.maxsubmissionsizebytes, 10)
|
||||||
|
@ -58,6 +52,12 @@ export class AddonModAssignSubmissionFileComponent extends AddonModAssignSubmiss
|
||||||
? parseInt(this.configs.maxfilesubmissions, 10)
|
? parseInt(this.configs.maxfilesubmissions, 10)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
// Get the offline data.
|
||||||
|
const filesData = await CoreUtils.ignoreErrors(
|
||||||
|
AddonModAssignOffline.getSubmission(this.assign.id),
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (filesData && filesData.plugindata && filesData.plugindata.files_filemanager) {
|
if (filesData && filesData.plugindata && filesData.plugindata.files_filemanager) {
|
||||||
const offlineDataFiles = <CoreFileUploaderStoreFilesResult>filesData.plugindata.files_filemanager;
|
const offlineDataFiles = <CoreFileUploaderStoreFilesResult>filesData.plugindata.files_filemanager;
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
<ion-label position="stacked" id="addon-mod-glossary-aliases-label">
|
<ion-label position="stacked" id="addon-mod-glossary-aliases-label">
|
||||||
{{ 'addon.mod_glossary.aliases' | translate }}
|
{{ 'addon.mod_glossary.aliases' | translate }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-textarea [(ngModel)]="options.aliases" rows="1" core-auto-rows
|
<ion-textarea [(ngModel)]="options.aliases" rows="1" [core-auto-rows]="options.aliases"
|
||||||
aria-labelledby="addon-mod-glossary-aliases-label" name="aliases">
|
aria-labelledby="addon-mod-glossary-aliases-label" name="aliases">
|
||||||
</ion-textarea>
|
</ion-textarea>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { CoreCourseContentsPage } from '@features/course/pages/contents/contents
|
||||||
import { CoreCourse } from '@features/course/services/course';
|
import { CoreCourse } from '@features/course/services/course';
|
||||||
import { IonContent } from '@ionic/angular';
|
import { IonContent } from '@ionic/angular';
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
|
import { CoreSync } from '@services/sync';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { Translate } from '@singletons';
|
import { Translate } from '@singletons';
|
||||||
|
@ -584,9 +585,17 @@ export class AddonModScormIndexComponent extends CoreCourseModuleMainActivityCom
|
||||||
/**
|
/**
|
||||||
* Performs the sync of the activity.
|
* Performs the sync of the activity.
|
||||||
*
|
*
|
||||||
|
* @param retries Number of retries done.
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async sync(): Promise<AddonModScormSyncResult> {
|
protected async sync(retries = 0): Promise<AddonModScormSyncResult> {
|
||||||
|
if (CoreSync.isBlocked(AddonModScormProvider.COMPONENT, this.scorm!.id) && retries < 5) {
|
||||||
|
// Sync is currently blocked, this can happen when SCORM player is left. Retry in a bit.
|
||||||
|
await CoreUtils.wait(400);
|
||||||
|
|
||||||
|
return this.sync(retries + 1);
|
||||||
|
}
|
||||||
|
|
||||||
const result = await AddonModScormSync.syncScorm(this.scorm!);
|
const result = await AddonModScormSync.syncScorm(this.scorm!);
|
||||||
|
|
||||||
if (!result.updated && this.dataSent) {
|
if (!result.updated && this.dataSent) {
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CoreError } from '@classes/errors/error';
|
import { CoreError } from '@classes/errors/error';
|
||||||
import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
|
import { CoreCourseActivityPrefetchHandlerBase } from '@features/course/classes/activity-prefetch-handler';
|
||||||
import { CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course';
|
import { CoreCourse, CoreCourseAnyModuleData, CoreCourseCommonModWSOptions } from '@features/course/services/course';
|
||||||
import { CoreApp } from '@services/app';
|
import { CoreApp } from '@services/app';
|
||||||
import { CoreFile } from '@services/file';
|
import { CoreFile } from '@services/file';
|
||||||
import { CoreFilepool } from '@services/filepool';
|
import { CoreFilepool } from '@services/filepool';
|
||||||
|
@ -316,8 +316,13 @@ export class AddonModScormPrefetchHandlerService extends CoreCourseActivityPrefe
|
||||||
* @param siteId Site ID.
|
* @param siteId Site ID.
|
||||||
* @returns Promise resolved with the SCORM.
|
* @returns Promise resolved with the SCORM.
|
||||||
*/
|
*/
|
||||||
protected getScorm(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModScormScorm> {
|
protected async getScorm(module: CoreCourseAnyModuleData, courseId: number, siteId?: string): Promise<AddonModScormScorm> {
|
||||||
const moduleUrl = 'url' in module ? module.url : undefined;
|
let moduleUrl = 'url' in module ? module.url : undefined;
|
||||||
|
if (!moduleUrl) {
|
||||||
|
module = await CoreCourse.getModule(module.id, module.course, undefined, true, false, siteId);
|
||||||
|
|
||||||
|
moduleUrl = module.url;
|
||||||
|
}
|
||||||
|
|
||||||
return AddonModScorm.getScorm(courseId, module.id, { moduleUrl, siteId });
|
return AddonModScorm.getScorm(courseId, module.id, { moduleUrl, siteId });
|
||||||
}
|
}
|
||||||
|
|
|
@ -198,7 +198,7 @@ export class AddonModScormSyncProvider extends CoreCourseActivitySyncBaseProvide
|
||||||
if (updated) {
|
if (updated) {
|
||||||
try {
|
try {
|
||||||
// Update downloaded data.
|
// Update downloaded data.
|
||||||
const module = await CoreCourse.getModuleBasicInfoByInstance(scorm.id, 'scorm', siteId);
|
const module = await CoreCourse.getModule(scorm.coursemodule, scorm.course, undefined, false, false, siteId);
|
||||||
|
|
||||||
await this.prefetchAfterUpdate(AddonModScormPrefetchHandler.instance, module, scorm.course, undefined, siteId);
|
await this.prefetchAfterUpdate(AddonModScormPrefetchHandler.instance, module, scorm.course, undefined, siteId);
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
@ -32,7 +32,8 @@
|
||||||
<ion-label position="stacked">
|
<ion-label position="stacked">
|
||||||
{{ 'addon.mod_workshop_assessment_accumulative.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}
|
{{ 'addon.mod_workshop_assessment_accumulative.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-textarea [(ngModel)]="selectedValues[n].peercomment" core-auto-rows></ion-textarea>
|
<ion-textarea [(ngModel)]="selectedValues[n].peercomment" [core-auto-rows]="selectedValues[n].peercomment">
|
||||||
|
</ion-textarea>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item *ngIf="!edit" class="ion-text-wrap">
|
<ion-item *ngIf="!edit" class="ion-text-wrap">
|
||||||
<ion-label>
|
<ion-label>
|
||||||
|
|
|
@ -14,7 +14,8 @@
|
||||||
{{ 'addon.mod_workshop_assessment_comments.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}
|
{{ 'addon.mod_workshop_assessment_comments.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}
|
||||||
</span>
|
</span>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-textarea [(ngModel)]="selectedValues[n].peercomment" core-auto-rows></ion-textarea>
|
<ion-textarea [(ngModel)]="selectedValues[n].peercomment" [core-auto-rows]="selectedValues[n].peercomment">
|
||||||
|
</ion-textarea>
|
||||||
<core-input-errors *ngIf="fieldErrors['peercomment_' + n]" [errorText]="fieldErrors['peercomment_' + n]">
|
<core-input-errors *ngIf="fieldErrors['peercomment_' + n]" [errorText]="fieldErrors['peercomment_' + n]">
|
||||||
</core-input-errors>
|
</core-input-errors>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
@ -36,7 +36,7 @@
|
||||||
{{ 'addon.mod_workshop_assessment_numerrors.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}
|
{{ 'addon.mod_workshop_assessment_numerrors.dimensioncommentfor' | translate : {'$a': field.dimtitle } }}
|
||||||
</ion-label>
|
</ion-label>
|
||||||
<ion-textarea [(ngModel)]="selectedValues[n].peercomment" [name]="'peercomment_' + n"
|
<ion-textarea [(ngModel)]="selectedValues[n].peercomment" [name]="'peercomment_' + n"
|
||||||
core-auto-rows>
|
[core-auto-rows]="selectedValues[n].peercomment">
|
||||||
</ion-textarea>
|
</ion-textarea>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-item *ngIf="!edit" class="ion-text-wrap">
|
<ion-item *ngIf="!edit" class="ion-text-wrap">
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</iframe>
|
</iframe>
|
||||||
|
|
||||||
<ion-button
|
<ion-button
|
||||||
*ngIf="!loading"
|
*ngIf="!loading && displayHelp"
|
||||||
color="dark" expand="block" fill="clear"
|
color="dark" expand="block" fill="clear"
|
||||||
(click)="openIframeHelpModal()"
|
(click)="openIframeHelpModal()"
|
||||||
aria-haspopup="dialog"
|
aria-haspopup="dialog"
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
[core-auto-focus]="showKeyboard"
|
[core-auto-focus]="showKeyboard"
|
||||||
[placeholder]="placeholder"
|
[placeholder]="placeholder"
|
||||||
rows="1"
|
rows="1"
|
||||||
core-auto-rows
|
[core-auto-rows]="message"
|
||||||
[(ngModel)]="message"
|
[(ngModel)]="message"
|
||||||
name="message"
|
name="message"
|
||||||
(onResize)="textareaResized()"
|
(onResize)="textareaResized()"
|
||||||
|
|
|
@ -12,41 +12,31 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
import { Directive, ElementRef, HostListener, Output, EventEmitter, AfterViewInit } from '@angular/core';
|
import { Directive, ElementRef, Output, EventEmitter, AfterViewInit, Input, OnChanges } from '@angular/core';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive to adapt a textarea rows depending on the input text. It's based on Moodle's data-auto-rows.
|
* Directive to adapt a textarea rows depending on the input text. It's based on Moodle's data-auto-rows.
|
||||||
*
|
*
|
||||||
* @description
|
* @description
|
||||||
* Usage:
|
* Usage:
|
||||||
* <textarea class="core-textarea" [(ngModel)]="message" rows="1" core-auto-rows></textarea>
|
* <textarea class="core-textarea" [(ngModel)]="message" rows="1" [core-auto-rows]="message"></textarea>
|
||||||
*/
|
*/
|
||||||
@Directive({
|
@Directive({
|
||||||
selector: 'textarea[core-auto-rows], ion-textarea[core-auto-rows]',
|
selector: 'textarea[core-auto-rows], ion-textarea[core-auto-rows]',
|
||||||
})
|
})
|
||||||
export class CoreAutoRowsDirective implements AfterViewInit {
|
export class CoreAutoRowsDirective implements AfterViewInit, OnChanges {
|
||||||
|
|
||||||
protected height = 0;
|
protected height = 0;
|
||||||
|
|
||||||
|
@Input('core-auto-rows') value?: string; // eslint-disable-line @angular-eslint/no-input-rename
|
||||||
@Output() onResize: EventEmitter<void>; // Emit when resizing the textarea.
|
@Output() onResize: EventEmitter<void>; // Emit when resizing the textarea.
|
||||||
|
|
||||||
constructor(protected element: ElementRef) {
|
constructor(protected element: ElementRef) {
|
||||||
this.onResize = new EventEmitter();
|
this.onResize = new EventEmitter();
|
||||||
}
|
}
|
||||||
|
|
||||||
@HostListener('input') onInput(): void {
|
|
||||||
this.resize();
|
|
||||||
}
|
|
||||||
|
|
||||||
@HostListener('change') onChange(): void {
|
|
||||||
// Fired on reset. Wait to the change to be finished.
|
|
||||||
setTimeout(() => {
|
|
||||||
this.resize();
|
|
||||||
}, 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize after content.
|
* Resize after initialized.
|
||||||
*/
|
*/
|
||||||
ngAfterViewInit(): void {
|
ngAfterViewInit(): void {
|
||||||
// Wait for rendering of child views.
|
// Wait for rendering of child views.
|
||||||
|
@ -55,6 +45,18 @@ export class CoreAutoRowsDirective implements AfterViewInit {
|
||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resize when content changes.
|
||||||
|
*/
|
||||||
|
ngOnChanges(): void {
|
||||||
|
this.resize();
|
||||||
|
|
||||||
|
if (this.value === '') {
|
||||||
|
// Maybe the form was resetted. In that case it takes a bit to update the height.
|
||||||
|
setTimeout(() => this.resize(), 300);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resize the textarea.
|
* Resize the textarea.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -206,7 +206,7 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy {
|
||||||
* Go to search courses.
|
* Go to search courses.
|
||||||
*/
|
*/
|
||||||
openSearch(): void {
|
openSearch(): void {
|
||||||
CoreNavigator.navigate('courses/search');
|
CoreNavigator.navigateToSitePath('courses/search');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,7 +19,7 @@ import { LangChangeEvent } from '@ngx-translate/core';
|
||||||
import { CoreAppProvider } from '@services/app';
|
import { CoreAppProvider } from '@services/app';
|
||||||
import { CoreConfig } from '@services/config';
|
import { CoreConfig } from '@services/config';
|
||||||
import { CoreSubscriptions } from '@singletons/subscriptions';
|
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||||
import { makeSingleton, Translate, Platform } from '@singletons';
|
import { makeSingleton, Translate, Platform, Http } from '@singletons';
|
||||||
|
|
||||||
import * as moment from 'moment';
|
import * as moment from 'moment';
|
||||||
import { CoreSite } from '../classes/site';
|
import { CoreSite } from '../classes/site';
|
||||||
|
@ -142,24 +142,24 @@ export class CoreLangProvider {
|
||||||
|
|
||||||
// Change the language, resolving the promise when we receive the first value.
|
// Change the language, resolving the promise when we receive the first value.
|
||||||
promises.push(new Promise((resolve, reject) => {
|
promises.push(new Promise((resolve, reject) => {
|
||||||
CoreSubscriptions.once(Translate.use(language), data => {
|
CoreSubscriptions.once(Translate.use(language), async data => {
|
||||||
// It's a language override, load the original one first.
|
// Check if it has a parent language.
|
||||||
const fallbackLang = this.getParentLanguage(language);
|
const fallbackLang = this.getParentLanguage(language);
|
||||||
|
|
||||||
if (fallbackLang) {
|
if (fallbackLang) {
|
||||||
CoreSubscriptions.once(
|
try {
|
||||||
Translate.use(fallbackLang),
|
// Merge parent translations with the child ones.
|
||||||
fallbackData => {
|
const parentTranslations = Translate.translations[fallbackLang] ?? await this.readLangFile(fallbackLang);
|
||||||
data = Object.assign(fallbackData, data);
|
|
||||||
|
const mergedData = Object.assign(parentTranslations, data);
|
||||||
|
|
||||||
|
Object.assign(data, mergedData);
|
||||||
|
} catch {
|
||||||
|
// Ignore errors.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resolve(data);
|
resolve(data);
|
||||||
},
|
|
||||||
// Resolve with the original language.
|
|
||||||
() => resolve(data),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
resolve(data);
|
|
||||||
}
|
|
||||||
}, reject);
|
}, reject);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -474,6 +474,20 @@ export class CoreLangProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a language file.
|
||||||
|
*
|
||||||
|
* @param lang Language code.
|
||||||
|
* @return Promise resolved with the file contents.
|
||||||
|
*/
|
||||||
|
async readLangFile(lang: string): Promise<Record<string, string>> {
|
||||||
|
const observable = Http.get(`assets/lang/${lang}.json`, {
|
||||||
|
responseType: 'json',
|
||||||
|
});
|
||||||
|
|
||||||
|
return <Record<string, string>> await observable.toPromise();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Unload custom or site plugin strings, removing them from the translations table.
|
* Unload custom or site plugin strings, removing them from the translations table.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue