Merge pull request #2213 from dpalou/MOBILE-3213

Mobile 3213
main
Juan Leyva 2019-12-13 18:53:14 +01:00 committed by GitHub
commit fb1096381e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 112 additions and 64 deletions

View File

@ -156,7 +156,7 @@
<h2>{{ 'addon.badges.relatedbages' | translate}}</h2>
</ion-item-divider>
<ion-item text-wrap *ngFor="let relatedBadge of badge.relatedbadges">
<h2><{{ relatedBadge.name }}</h2>
<h2>{{ relatedBadge.name }}</h2>
</ion-item>
<ion-item text-wrap *ngIf="badge.relatedbadges.length == 0">
<h2>{{ 'addon.badges.norelated' | translate}}</h2>

View File

@ -91,6 +91,12 @@ export class AddonBadgesProvider {
// In 3.7, competencies was renamed to alignment. Rename the property in 3.6 too.
response.badges.forEach((badge) => {
badge.alignment = badge.alignment || badge.competencies;
// Check that the alignment is valid, they were broken in 3.7.
if (badge.alignment && badge.alignment[0] && typeof badge.alignment[0].targetname == 'undefined') {
// If any badge lacks targetname it means they are affected by the Moodle bug, don't display them.
delete badge.alignment;
}
});
return response.badges;
@ -175,20 +181,20 @@ export type AddonBadgesUserBadge = {
alignment?: { // @since 3.7. Calculated by the app for 3.6 sites. Badge alignments.
id?: number; // Alignment id.
badgeid?: number; // Badge id.
targetName?: string; // Target name.
targetUrl?: string; // Target URL.
targetDescription?: string; // Target description.
targetFramework?: string; // Target framework.
targetCode?: string; // Target code.
targetname?: string; // Target name.
targeturl?: string; // Target URL.
targetdescription?: string; // Target description.
targetframework?: string; // Target framework.
targetcode?: string; // Target code.
}[];
competencies?: { // @deprecated from 3.7. @since 3.6. In 3.7 it was renamed to alignment.
id?: number; // Alignment id.
badgeid?: number; // Badge id.
targetName?: string; // Target name.
targetUrl?: string; // Target URL.
targetDescription?: string; // Target description.
targetFramework?: string; // Target framework.
targetCode?: string; // Target code.
targetname?: string; // Target name.
targeturl?: string; // Target URL.
targetdescription?: string; // Target description.
targetframework?: string; // Target framework.
targetcode?: string; // Target code.
}[];
relatedbadges?: { // @since 3.6. Related badges.
id: number; // Badge id.

View File

@ -5,7 +5,7 @@
<core-loading [hideUntil]="loaded" class="core-loading-center">
<ion-item *ngIf="showMyEntriesToggle">
<ion-label>{{ 'addon.blog.showonlyyourentries' | translate }}</ion-label>
<ion-toggle [(ngModel)]="onlyMyEntries" (ionChange)="onlyMyEntriesToggleChanged(onlyMyEntries)">></ion-toggle>
<ion-toggle [(ngModel)]="onlyMyEntries" (ionChange)="onlyMyEntriesToggleChanged(onlyMyEntries)"></ion-toggle>
</ion-item>
<core-empty-box *ngIf="entries && entries.length == 0" icon="fa-newspaper-o" [message]="'addon.blog.noentriesyet' | translate"></core-empty-box>
<ng-container *ngFor="let entry of entries">

View File

@ -119,6 +119,25 @@ export class AddonModAssignProvider {
return assign.submissiondrafts;
}
/**
* Fix some submission status params.
*
* @param site Site to use.
* @param userId User Id (empty for current user).
* @param groupId Group Id (empty for all participants).
* @param isBlind If blind marking is enabled or not.
* @return Object with fixed params.
*/
protected fixSubmissionStatusParams(site: CoreSite, userId?: number, groupId?: number, isBlind?: boolean)
: {userId: number, groupId: number, isBlind: boolean} {
return {
isBlind: !userId ? false : !!isBlind,
groupId: site.isVersionGreaterEqualThan('3.5') ? groupId || 0 : 0,
userId: userId || site.getUserId(),
};
}
/**
* Get an assignment by course module ID.
*
@ -502,24 +521,23 @@ export class AddonModAssignProvider {
getSubmissionStatus(assignId: number, userId?: number, groupId?: number, isBlind?: boolean, filter: boolean = true,
ignoreCache?: boolean, siteId?: string): Promise<AddonModAssignGetSubmissionStatusResult> {
userId = userId || 0;
return this.sitesProvider.getSite(siteId).then((site) => {
groupId = site.isVersionGreaterEqualThan('3.5') ? groupId || 0 : 0;
const fixedParams = this.fixSubmissionStatusParams(site, userId, groupId, isBlind);
const params = {
assignid: assignId,
userid: userId
userid: fixedParams.userId
},
preSets: CoreSiteWSPreSets = {
cacheKey: this.getSubmissionStatusCacheKey(assignId, userId, groupId, isBlind),
cacheKey: this.getSubmissionStatusCacheKey(assignId, fixedParams.userId, fixedParams.groupId,
fixedParams.isBlind),
getCacheUsingCacheKey: true, // We use the cache key to take isBlind into account.
filter: filter,
rewriteurls: filter
};
if (groupId) {
params['groupid'] = groupId;
if (fixedParams.groupId) {
params['groupid'] = fixedParams.groupId;
}
if (ignoreCache) {
@ -578,11 +596,6 @@ export class AddonModAssignProvider {
* @return Cache key.
*/
protected getSubmissionStatusCacheKey(assignId: number, userId: number, groupId?: number, isBlind?: boolean): string {
if (!userId) {
isBlind = false;
userId = this.sitesProvider.getCurrentSiteUserId();
}
return this.getSubmissionsCacheKey(assignId) + ':' + userId + ':' + (isBlind ? 1 : 0) + ':' + groupId;
}
@ -809,7 +822,10 @@ export class AddonModAssignProvider {
invalidateSubmissionStatusData(assignId: number, userId?: number, groupId?: number, isBlind?: boolean, siteId?: string):
Promise<any> {
return this.sitesProvider.getSite(siteId).then((site) => {
return site.invalidateWsCacheForKey(this.getSubmissionStatusCacheKey(assignId, userId, groupId, isBlind));
const fixedParams = this.fixSubmissionStatusParams(site, userId, groupId, isBlind);
return site.invalidateWsCacheForKey(this.getSubmissionStatusCacheKey(assignId, fixedParams.userId,
fixedParams.groupId, fixedParams.isBlind));
});
}

View File

@ -17,7 +17,7 @@
<ion-item text-wrap *ngIf="item.typ != 'pagebreak'" [color]="item.dependitem > 0 ? 'light' : ''" [class.core-danger-item]="item.isEmpty || item.hasError">
<ion-label *ngIf="item.name" [core-mark-required]="item.required" stacked>
<span *ngIf="feedback.autonumbering && item.itemnumber">{{item.itemnumber}}. </span>
<core-format-text [component]="component" [componentId]="componentId" [text]="item.name" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId"></core-format-text>
<core-format-text [component]="component" [componentId]="componentId" [text]="item.name" contextLevel="module" [contextInstanceId]="module.id" [courseId]="courseId" [wsNotFiltered]="true"></core-format-text>
<span *ngIf="item.postfix" class="addon-mod_feedback-postfix">{{item.postfix}}</span>
</ion-label>
<div item-content class="addon-mod_feedback-form-content" *ngIf="item.template">

View File

@ -105,7 +105,7 @@ export class AddonModFolderIndexComponent extends CoreCourseModuleMainResourceCo
if (this.canGetFolder) {
promise = this.folderProvider.getFolder(this.courseId, this.module.id).then((folder) => {
return this.courseProvider.loadModuleContents(this.module, this.courseId).then(() => {
return this.courseProvider.loadModuleContents(this.module, this.courseId, undefined, false, refresh).then(() => {
folderContents = this.module.contents;
return folder;

View File

@ -53,7 +53,7 @@
<ion-icon name="trash"></ion-icon>
</button>
</ion-item>
<ion-item text-wrap>{{ note.content }}</ion-item>
<ion-item text-wrap><core-format-text [text]="note.content" [filter]="false"></core-format-text></ion-item>
</ion-card>
</ion-list>

View File

@ -180,18 +180,19 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy
if (this.platform.is('android')) {
// In Android we ignore the keyboard height because it is not part of the web view.
height = this.domUtils.getContentHeight(this.content) - this.getSurroundingHeight(this.element);
} else if (this.platform.is('ios') && this.kbHeight > 0) {
// Keyboard open in iOS.
// In this case, the header disappears or is scrollable, so we need to adjust the calculations.
} else if (this.platform.is('ios') && this.kbHeight > 0 && this.platform.version().major < 12) {
// Keyboard open in iOS 11 or previous. The window height changes when the keyboard is open.
height = window.innerHeight - this.getSurroundingHeight(this.element);
if (this.element.getBoundingClientRect().top < 40) {
// In iOS sometimes the editor is placed below the status bar. Move the scroll a bit so it doesn't happen.
window.scrollTo(window.scrollX, window.scrollY - 40);
}
} else {
// Header is fixed, use the content to calculate the editor height.
height = this.domUtils.getContentHeight(this.content) - this.kbHeight - this.getSurroundingHeight(this.element);
}
if (height > this.minHeight) {
@ -549,12 +550,14 @@ export class CoreRichTextEditorComponent implements AfterContentInit, OnDestroy
}
/**
* Hide the toolbar.
* Hide the toolbar in phone mode.
*/
hideToolbar($event: any): void {
this.stopBubble($event);
this.toolbarHidden = true;
if (this.isPhone) {
this.toolbarHidden = true;
}
}
/**

View File

@ -661,7 +661,6 @@ export class CoreCourseHelperProvider {
const mainFile = files[0],
fileUrl = this.fileHelper.getFileUrl(mainFile),
timemodified = this.fileHelper.getFileTimemodified(mainFile),
result = {
fixedUrl: undefined,
path: undefined,
@ -678,48 +677,23 @@ export class CoreCourseHelperProvider {
return this.filepoolProvider.getPackageStatus(siteId, component, componentId).then((status) => {
result.status = status;
const isWifi = this.appProvider.isWifi(),
isOnline = this.appProvider.isOnline();
if (status === CoreConstants.DOWNLOADED) {
// Get the local file URL.
return this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl).catch((error) => {
// File not found, mark the module as not downloaded and reject.
// File not found, mark the module as not downloaded and try again.
return this.filepoolProvider.storePackageStatus(siteId, CoreConstants.NOT_DOWNLOADED, component,
componentId).then(() => {
return Promise.reject(error);
return this.downloadModuleWithMainFile(module, courseId, fixedUrl, files, status, component,
componentId, siteId);
});
});
} else if (status === CoreConstants.DOWNLOADING && !this.appProvider.isDesktop()) {
// Return the online URL.
return fixedUrl;
} else {
if (!isOnline && status === CoreConstants.NOT_DOWNLOADED) {
// Not downloaded and we're offline, reject.
return Promise.reject(this.translate.instant('core.networkerrormsg'));
}
return this.filepoolProvider.shouldDownloadBeforeOpen(fixedUrl, mainFile.filesize).then(() => {
// Download and then return the local URL.
return this.downloadModule(module, courseId, component, componentId, files, siteId).then(() => {
return this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl);
});
}, () => {
// Start the download if in wifi, but return the URL right away so the file is opened.
if (isWifi) {
this.downloadModule(module, courseId, component, componentId, files, siteId);
}
if (!this.fileHelper.isStateDownloaded(status) || isOnline) {
// Not downloaded or online, return the online URL.
return fixedUrl;
} else {
// Outdated but offline, so we return the local URL. Use getUrlByUrl so it's added to the queue.
return this.filepoolProvider.getUrlByUrl(siteId, fileUrl, component, componentId, timemodified,
false, false, mainFile);
}
});
return this.downloadModuleWithMainFile(module, courseId, fixedUrl, files, status, component, componentId,
siteId);
}
}).then((path) => {
result.path = path;
@ -735,6 +709,55 @@ export class CoreCourseHelperProvider {
});
}
/**
* Convenience function to download a module that has a main file and return the local file's path and other info.
* This is meant for modules like mod_resource.
*
* @param module The module to download.
* @param courseId The course ID of the module.
* @param fixedUrl Main file's fixed URL.
* @param files List of files of the module.
* @param status The package status.
* @param component The component to link the files to.
* @param componentId An ID to use in conjunction with the component.
* @param siteId The site ID. If not defined, current site.
* @return Promise resolved when done.
*/
protected downloadModuleWithMainFile(module: any, courseId: number, fixedUrl: string, files: any[], status: string,
component?: string, componentId?: string | number, siteId?: string): Promise<string> {
const isOnline = this.appProvider.isOnline();
const mainFile = files[0];
const fileUrl = this.fileHelper.getFileUrl(mainFile);
const timemodified = this.fileHelper.getFileTimemodified(mainFile);
if (!isOnline && status === CoreConstants.NOT_DOWNLOADED) {
// Not downloaded and we're offline, reject.
return Promise.reject(this.translate.instant('core.networkerrormsg'));
}
return this.filepoolProvider.shouldDownloadBeforeOpen(fixedUrl, mainFile.filesize).then(() => {
// Download and then return the local URL.
return this.downloadModule(module, courseId, component, componentId, files, siteId).then(() => {
return this.filepoolProvider.getInternalUrlByUrl(siteId, fileUrl);
});
}, () => {
// Start the download if in wifi, but return the URL right away so the file is opened.
if (this.appProvider.isWifi()) {
this.downloadModule(module, courseId, component, componentId, files, siteId);
}
if (!this.fileHelper.isStateDownloaded(status) || isOnline) {
// Not downloaded or online, return the online URL.
return fixedUrl;
} else {
// Outdated but offline, so we return the local URL. Use getUrlByUrl so it's added to the queue.
return this.filepoolProvider.getUrlByUrl(siteId, fileUrl, component, componentId, timemodified,
false, false, mainFile);
}
});
}
/**
* Convenience function to download a module.
*