Merge pull request #3368 from crazyserver/MOBILE-4081

Mobile 4081
main
Dani Palou 2022-08-31 16:45:05 +02:00 committed by GitHub
commit 814491afdb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
63 changed files with 202 additions and 252 deletions

View File

@ -16,7 +16,7 @@
<ion-card>
<ion-item class="core-course-module-handler ion-text-wrap" detail="false" (click)="action($event, item)" button>
<core-mod-icon slot="start" *ngIf="item.iconUrl" [modicon]="item.iconUrl" [modname]="item.modname"
[componentId]="item.cmid" [showAlt]="false">
[componentId]="item.cmid" [showAlt]="false" [purpose]="item.purpose">
</core-mod-icon>
<ion-label>
<!-- Add the icon title so accessibility tools read it. -->

View File

@ -103,6 +103,8 @@ export const AddonBlockRecentlyAccessedItems = makeSingleton(AddonBlockRecentlyA
/**
* Result of WS block_recentlyaccesseditems_get_recent_items.
*
* The most recently accessed activities/resources by the logged user.
*/
export type AddonBlockRecentlyAccessedItemsItem = {
id: number; // Id.
@ -116,6 +118,7 @@ export type AddonBlockRecentlyAccessedItemsItem = {
viewurl: string; // Viewurl.
courseviewurl: string; // Courseviewurl.
icon: string; // Icon.
purpose?: string; // Purpose. @since 4.0
} & AddonBlockRecentlyAccessedItemsItemCalculatedData;
/**

View File

@ -23,7 +23,7 @@
<ion-col class="addon-block-timeline-activity-time ion-no-padding">
<small>{{event.timesort * 1000 | coreFormatDate:"strftimetime24" }}</small>
<core-mod-icon *ngIf="event.iconUrl" [modicon]="event.iconUrl" [componentId]="event.instance"
[modname]="event.modulename">
[modname]="event.modulename" [purpose]="event.purpose">
</core-mod-icon>
</ion-col>
<ion-col class="addon-block-timeline-activity-name ion-no-padding">

View File

@ -93,7 +93,7 @@
</div>
</ng-container>
<p *ngIf="day.filteredEvents.length > 4" class="addon-calendar-day-more">
<b>{{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }}</b>
<strong>{{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }}</strong>
</p>
</div>
</ion-col>

View File

@ -8,7 +8,7 @@
<ion-item class="ion-text-wrap addon-calendar-event" [attr.aria-label]="event.name" (click)="eventClicked(event)" button
[ngClass]="['addon-calendar-eventtype-'+event.eventtype]" [detail]="false">
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" slot="start" [modname]="event.modulename"
[componentId]="event.instance" [showAlt]="false"></core-mod-icon>
[componentId]="event.instance" [showAlt]="false" [purpose]="event.purpose"></core-mod-icon>
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start" aria-hidden="true">
</ion-icon>
<ion-label>

View File

@ -70,7 +70,7 @@
(click)="gotoEvent(event.id, day)" [class.item-dimmed]="event.ispast"
[ngClass]="['addon-calendar-eventtype-'+event.eventtype]" button [detail]="false">
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" slot="start" [showAlt]="false"
[modname]="event.modulename" [componentId]="event.instance">
[modname]="event.modulename" [componentId]="event.instance" [purpose]="event.purpose">
</core-mod-icon>
<ion-icon *ngIf="event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" slot="start"
aria-hidden="true">

View File

@ -42,7 +42,7 @@
<ion-list *ngIf="event">
<ion-item class="ion-text-wrap addon-calendar-event" collapsible [ngClass]="['addon-calendar-eventtype-'+event.eventtype]">
<core-mod-icon *ngIf="event.moduleIcon" [modicon]="event.moduleIcon" [showAlt]="false" [modname]="event.modulename"
[componentId]="event.instance" slot="start"></core-mod-icon>
[componentId]="event.instance" slot="start" [purpose]="event.purpose"></core-mod-icon>
<ion-icon *ngIf=" event.eventIcon && !event.moduleIcon" [name]="event.eventIcon" aria-hidden="true" slot="start">
</ion-icon>
<ion-label>

View File

@ -178,6 +178,7 @@ export class AddonCalendarHelperProvider {
format: 1,
visible: 1,
offline: false,
purpose: 'purpose' in event ? event.purpose : undefined,
};
if (event.modulename) {
@ -660,7 +661,7 @@ export class AddonCalendarHelperProvider {
const finalPromises: Promise<unknown>[] =[AddonCalendar.invalidateAllUpcomingEvents()];
// Fetch months and days.
fetchTimestarts.map((fetchTime) => {
fetchTimestarts.forEach((fetchTime) => {
const day = moment(fetchTime * 1000);
const monthId = this.getMonthId(day);
@ -696,7 +697,7 @@ export class AddonCalendarHelperProvider {
});
// Invalidate months and days.
invalidateTimestarts.map((fetchTime) => {
invalidateTimestarts.forEach((fetchTime) => {
const day = moment(fetchTime * 1000);
const monthId = this.getMonthId(day);

View File

@ -529,7 +529,7 @@ export class AddonCalendarProvider {
// Convert the array to an object.
const result = {};
if (response.allowedeventtypes) {
response.allowedeventtypes.map((type) => {
response.allowedeventtypes.forEach((type) => {
result[type] = true;
});
}
@ -1949,6 +1949,7 @@ export type AddonCalendarEventBase = {
*/
export type AddonCalendarEvent = AddonCalendarEventBase & {
url: string; // Url.
purpose?: string; // Purpose. @since 4.0
action?: {
name: string; // Name.
url: string; // Url.
@ -2302,6 +2303,7 @@ export type AddonCalendarEventToDisplay = Partial<AddonCalendarCalendarEvent> &
ispast?: boolean; // Calculated in the app. Whether the event is in the past.
contextLevel?: ContextLevel;
contextInstanceId?: number;
purpose?: string; // Purpose. @since 4.0
};
/**

View File

@ -65,7 +65,7 @@
<ng-container *ngIf="isSelf && !canLoadMore">
<p class="ion-text-center">{{ 'addon.messages.selfconversation' | translate }}</p>
<p class="ion-text-center"><i>{{ 'addon.messages.selfconversationdefaultmessage' | translate }}</i></p>
<p class="ion-text-center"><em>{{ 'addon.messages.selfconversationdefaultmessage' | translate }}</em></p>
</ng-container>
<h2 class="sr-only">{{ title }}</h2>

View File

@ -126,7 +126,7 @@ export class AddonModAssignSubmissionsSource extends CoreRoutedItemsManagerSourc
CoreSites.getCurrentSiteId(),
);
}
} catch (error) {
} catch {
// Ignore errors, probably user is offline or sync is blocked.
}
}

View File

@ -525,7 +525,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
submitId: this.submitId,
}, this.siteId);
}
} catch (error) {
} catch {
// Ignore errors, probably user is offline or sync is blocked.
}
}

View File

@ -565,7 +565,7 @@ export class AddonModAssignProvider {
};
try {
return this.getSubmissionStatus(assign.id, newOptions);
return await this.getSubmissionStatus(assign.id, newOptions);
} catch {
// Error, return the first result even if it doesn't have the user submission.
return response;

View File

@ -220,7 +220,7 @@ export class AddonModChatChatPage implements OnInit, OnDestroy, CanLeave {
}
return id;
} catch (error) {
} catch {
// Ignore errors.
return id;
}

View File

@ -66,7 +66,9 @@ export class AddonModChatHelperProvider {
if (!formattedMessage.special && formattedMessage.message.match(patternTo)) {
const matches = formattedMessage.message.match(patternTo);
formattedMessage.message = `<i><b>${Translate.instant('addon.mod_chat.saidto')} </b>${matches![1]}</i>: ${matches![2]}`;
formattedMessage.message = `<em>
<strong>${Translate.instant('addon.mod_chat.saidto')} </strong>
${matches![1]}</em>: ${matches![2]}`;
}
formattedMessage.showUserData = this.showUserData(currentUserId, message, prevMessage);

View File

@ -723,7 +723,7 @@ export class AddonModDataHelperProvider {
try {
await AddonModData.invalidateEntryData(dataId, entryId, siteId);
await AddonModData.invalidateEntriesData(dataId, siteId);
} catch (error) {
} catch {
// Ignore errors.
}

View File

@ -987,7 +987,7 @@ export class AddonModDataProvider {
options.groupId = options.groupId || 0;
options.sort = options.sort || 0;
options.order || options.order || 'DESC';
options.order = options.order || 'DESC';
options.page = options.page || 0;
options.perPage = options.perPage || AddonModDataProvider.PER_PAGE;
options.readingStrategy = options.readingStrategy || CoreSitesReadingStrategy.PREFER_NETWORK;

View File

@ -66,9 +66,9 @@ export class AddonModFeedbackAttemptPage implements OnInit, OnDestroy {
/**
* @inheritdoc
*/
ngOnInit(): void {
async ngOnInit(): Promise<void> {
try {
this.attempts.start();
await this.attempts.start();
} catch (error) {
CoreDomUtils.showErrorModal(error);

View File

@ -292,7 +292,7 @@ export class AddonModForumDiscussionsSource extends CoreRoutedItemsManagerSource
discussion.userfullname = user.fullname;
discussion.userpictureurl = user.profileimageurl;
} catch (error) {
} catch {
// Ignore errors.
}
});

View File

@ -358,7 +358,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
// Not set, use default sort.
// @TODO add fallback to $CFG->forum_displaymode.
}
} catch (error) {
} catch {
// Ignore errors.
}
}
@ -511,7 +511,7 @@ export class AddonModForumDiscussionPage implements OnInit, AfterViewInit, OnDes
}
await Promise.all(promises);
} catch (error) {
} catch {
// Ignore errors.
}

View File

@ -474,7 +474,7 @@ class AddonModGlossaryEntriesManager extends CoreListItemsManager<AddonModGlossa
await AddonModGlossary.logView(glossary.id, viewMode, glossary.name);
CoreCourse.checkModuleCompletion(this.page.courseId, this.page.module.completiondata);
} catch (error) {
} catch {
// Ignore errors.
}
}

View File

@ -663,13 +663,12 @@ export class AddonModGlossaryProvider {
// No more pages and the entry wasn't found. Reject.
throw new CoreError('Entry not found.');
};
}
/**
* Performs the whole fetch of the entries using the proper function and arguments.
*
* @param fetchFunction Function to fetch.
* @param fetchArguments Arguments to call the fetching.
* @param options Other options.
* @return Promise resolved with all entrries.
*/
@ -682,7 +681,7 @@ export class AddonModGlossaryProvider {
const entries: AddonModGlossaryEntry[] = [];
const fetchMoreEntries = async (): Promise<AddonModGlossaryEntry[]> => {
const result = await fetchFunction({
const result = fetchFunction({
from: entries.length,
...options, // Include all options.
});
@ -915,7 +914,14 @@ export class AddonModGlossaryProvider {
try {
// Try to add it in online.
return this.addEntryOnline(glossaryId, concept, definition, entryOptions, <number> attachments, otherOptions.siteId);
return await this.addEntryOnline(
glossaryId,
concept,
definition,
entryOptions,
<number> attachments,
otherOptions.siteId,
);
} catch (error) {
if (otherOptions.allowOffline && !CoreUtils.isWebServiceError(error)) {
// Couldn't connect to server, store in offline.

View File

@ -459,7 +459,7 @@ export class AddonModH5PActivityIndexComponent extends CoreCourseModuleMainActiv
try {
// Invalidate attempts.
await AddonModH5PActivity.invalidateUserAttempts(this.h5pActivity.id, undefined, this.siteId);
} catch (error) {
} catch {
// Ignore errors.
}

View File

@ -119,7 +119,7 @@ export class AddonModH5PActivityAttemptResultsPage implements OnInit {
try {
this.user = await CoreUser.getProfile(this.attempt.userid, this.courseId, true);
} catch (error) {
} catch {
// Ignore errors.
}
}

View File

@ -133,7 +133,7 @@ export class AddonModH5PActivityUserAttemptsPage implements OnInit {
protected async fetchUserProfile(): Promise<void> {
try {
this.user = await CoreUser.getProfile(this.userId, this.courseId, true);
} catch (error) {
} catch {
// Ignore errors.
}
}

View File

@ -143,7 +143,7 @@ export class AddonModLessonPrefetchHandlerService extends CoreCourseActivityPref
if (password) {
try {
return this.validatePassword(lessonId, accessInfo, password, options);
return await this.validatePassword(lessonId, accessInfo, password, options);
} catch {
// Error validating it.
}
@ -320,7 +320,7 @@ export class AddonModLessonPrefetchHandlerService extends CoreCourseActivityPref
* @param lesson Lesson.
* @param password Password (if needed).
* @param retake Retake to prefetch.
* @param options Options.
* @param modOptions Options.
* @return Promise resolved when done.
*/
protected async prefetchPlayData(
@ -364,12 +364,7 @@ export class AddonModLessonPrefetchHandlerService extends CoreCourseActivityPref
const promises = pages.map(async (data) => {
// Check if any page has a RANDOMBRANCH jump.
if (!hasRandomBranch) {
for (let i = 0; i < data.jumps.length; i++) {
if (data.jumps[i] == AddonModLessonProvider.LESSON_RANDOMBRANCH) {
hasRandomBranch = true;
break;
}
}
hasRandomBranch = data.jumps.some((jump) => jump === AddonModLessonProvider.LESSON_RANDOMBRANCH);
}
// Get the page data. We don't pass accessInfo because we don't need to calculate the offline data.
@ -499,8 +494,8 @@ export class AddonModLessonPrefetchHandlerService extends CoreCourseActivityPref
* Validate the password.
*
* @param lessonId Lesson ID.
* @param info Lesson access info.
* @param pwd Password to check.
* @param accessInfo Lesson access info.
* @param password Password to check.
* @param options Other options.
* @return Promise resolved when done.
*/

View File

@ -118,7 +118,7 @@ export class AddonModLtiHelperProvider {
await AddonModLti.logView(ltiId, name, siteId);
CoreCourse.checkModuleCompletion(courseId, module.completiondata);
} catch (error) {
} catch {
// Ignore errors.
}
}

View File

@ -65,7 +65,7 @@
</article>
<div class="ion-margin-top" *ngIf="tagsEnabled && tags.length > 0">
<b>{{ 'core.tag.tags' | translate }}:</b>
<strong>{{ 'core.tag.tags' | translate }}:</strong>
<core-tag-list [tags]="tags"></core-tag-list>
</div>
</div>

View File

@ -723,7 +723,7 @@ export class AddonModWikiProvider {
try {
// Try to create it in online.
return this.newPageOnline(title, content, options);
return await this.newPageOnline(title, content, options);
} catch (error) {
if (CoreUtils.isWebServiceError(error)) {
// The WebService has thrown an error, this means that the page cannot be added.

View File

@ -119,7 +119,7 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe
// Load Weights selector.
if (this.edit && this.access.canallocate) {
this.weights;
this.weights = [];
for (let i = 16; i >= 0; i--) {
this.weights[i] = i;
}
@ -347,8 +347,6 @@ export class AddonModWorkshopAssessmentStrategyComponent implements OnInit, OnDe
this.workshop.course,
assessmentData,
);
gradeUpdated = false;
} else {
// Try to send it to server.

View File

@ -8,7 +8,6 @@
:host-context(ion-item.md) ion-icon {
&[slot] {
font-size: 1.6em;
color: rgba(var(--ion-text-color-rgb, 0, 0, 0), 0.54);
font-size: 24px;
margin-top: 12px;

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { CoreConstants } from '@/core/constants';
import { CoreConstants, ModPurpose } from '@/core/constants';
import { Component, ElementRef, Input, OnChanges, OnInit, SimpleChange } from '@angular/core';
import { CoreCourse } from '@features/course/services/course';
import { CoreCourseModuleDelegate } from '@features/course/services/module-delegate';
@ -31,10 +31,11 @@ const fallbackModName = 'external-tool';
})
export class CoreModIconComponent implements OnInit, OnChanges {
@Input() modname?; // The module name. Used also as component if set.
@Input() componentId?; // Component Id for external icons.
@Input() modname?: string; // The module name. Used also as component if set.
@Input() componentId?: number; // Component Id for external icons.
@Input() modicon?: string; // Module icon url or local url.
@Input() showAlt = true; // Show alt otherwise it's only presentation icon.
@Input() purpose: ModPurpose = ModPurpose.MOD_PURPOSE_OTHER; // Purpose of the module.
icon = '';
modNameTranslated = '';
@ -62,10 +63,15 @@ export class CoreModIconComponent implements OnInit, OnChanges {
this.modNameTranslated = this.modname ? CoreCourse.translateModuleName(this.modname) || '' : '';
if (CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('4.0')) {
this.legacyIcon = false;
const purposeClass =
CoreCourseModuleDelegate.supportsFeature<string>(this.modname, CoreConstants.FEATURE_MOD_PURPOSE, '');
if (purposeClass != '') {
const purposeClass =
CoreCourseModuleDelegate.supportsFeature<ModPurpose>(
this.modname || '',
CoreConstants.FEATURE_MOD_PURPOSE,
this.purpose,
);
if (purposeClass) {
const element: HTMLElement = this.el.nativeElement;
element.classList.add(purposeClass);
}
@ -94,8 +100,8 @@ export class CoreModIconComponent implements OnInit, OnChanges {
// If modname is not set icon won't be cached.
// Also if the url matches the regexp (the theme will manage the image so it's not cached).
this.linkIconWithComponent =
this.modname &&
this.componentId &&
!!this.modname &&
!!this.componentId &&
!this.isLocalUrl &&
!this.icon.match('/theme/image.php/[^/]+/' + this.modname + '/[-0-9]*/');
}

View File

@ -52,10 +52,8 @@ export abstract class CoreBlockBaseComponent implements OnInit, ICoreBlockCompon
*/
async ngOnInit(): Promise<void> {
if (this.block.configs && this.block.configs.length > 0) {
this.block.configs.map((config) => {
this.block.configs.forEach((config) => {
config.value = CoreTextUtils.parseJSON(config.value);
return config;
});
this.block.configsRecord = CoreUtils.arrayToObject(this.block.configs, 'name');

View File

@ -47,13 +47,7 @@ export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandler
}
/**
* Get the list of actions for a link (url).
*
* @param siteIds List of sites the URL belongs to.
* @param url The URL to treat.
* @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
* @param courseId Course ID related to the URL. Optional but recommended.
* @return List of (or promise resolved with list of) actions.
* @inheritdoc
*/
getActions(
siteIds: string[],
@ -103,17 +97,10 @@ export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandler
}
/**
* Check if the handler is enabled for a certain site (site + user) and a URL.
* If not defined, defaults to true.
*
* @param siteId The site ID.
* @param url The URL to treat.
* @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
* @param courseId Course ID related to the URL. Optional but recommended.
* @return Whether the handler is enabled for the URL and site.
* @inheritdoc
*/
async isEnabled(siteId: string, url: string, params: Params, courseId?: number): Promise<boolean> {
courseId = parseInt(params.id, 10);
async isEnabled(siteId: string, url: string, params: Params): Promise<boolean> {
const courseId = parseInt(params.id, 10);
if (!courseId) {
return false;
@ -333,7 +320,7 @@ export class CoreCoursesCourseLinkHandlerService extends CoreContentLinksHandler
await CoreUtils.wait(5000);
return await this.waitForEnrolled(courseId);
return this.waitForEnrolled(courseId);
}
}

View File

@ -973,7 +973,7 @@ export class CoreEditorRichTextEditorComponent implements OnInit, AfterViewInit,
this.showMessage('core.editor.textrecovered', this.RESTORE_MESSAGE_CLEAR_TIME);
}
}
} catch (error) {
} catch {
// Ignore errors, shouldn't happen.
}
}

View File

@ -56,7 +56,7 @@ export class CoreEditorOfflineProvider {
const params = this.fixDraftPrimaryData(contextLevel, contextInstanceId, elementId, extraParams);
await db.deleteRecords(DRAFT_TABLE, params);
} catch (error) {
} catch {
// Ignore errors, probably no draft stored.
}
}
@ -149,12 +149,12 @@ export class CoreEditorOfflineProvider {
}
await db.insertRecord(DRAFT_TABLE, entry);
} catch (error) {
} catch {
// Ignore errors saving the draft. It shouldn't happen.
}
return entry;
} catch (error) {
} catch {
// No draft stored. Store an empty draft to save the pageinstance.
await this.saveDraft(
contextLevel,

View File

@ -812,7 +812,7 @@ export class CoreFileUploaderHelperProvider {
file = await CoreFile.getFileObjectFromFileEntry(fileEntry);
size = file.size;
} catch (error) {
} catch {
// Ignore failures.
}
}

View File

@ -30,14 +30,7 @@ export class CoreGradesReportLinkHandlerService extends CoreContentLinksHandlerB
pattern = /\/grade\/report(\/user)?\/index.php/;
/**
* Get the list of actions for a link (url).
*
* @param siteIds List of sites the URL belongs to.
* @param url The URL to treat.
* @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
* @param courseId Course ID related to the URL. Optional but recommended.
* @param data Extra data to handle the URL.
* @return List of (or promise resolved with list of) actions.
* @inheritdoc
*/
getActions(
siteIds: string[],
@ -60,14 +53,7 @@ export class CoreGradesReportLinkHandlerService extends CoreContentLinksHandlerB
}
/**
* Check if the handler is enabled for a certain site (site + user) and a URL.
* If not defined, defaults to true.
*
* @param siteId The site ID.
* @param url The URL to treat.
* @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
* @param courseId Course ID related to the URL. Optional but recommended.
* @return Whether the handler is enabled for the URL and site.
* @inheritdoc
*/
async isEnabled(siteId: string, url: string, params: Record<string, string>, courseId?: number): Promise<boolean> {
if (!courseId && !params.id) {

View File

@ -30,14 +30,7 @@ export class CoreGradesUserLinkHandlerService extends CoreContentLinksHandlerBas
pattern = /\/course\/user\.php.*[?&]mode=grade/;
/**
* Get the list of actions for a link (url).
*
* @param siteIds List of sites the URL belongs to.
* @param url The URL to treat.
* @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
* @param courseId Course ID related to the URL. Optional but recommended.
* @param data Extra data to handle the URL.
* @return List of (or promise resolved with list of) actions.
* @inheritdoc
*/
getActions(
siteIds: string[],
@ -60,14 +53,7 @@ export class CoreGradesUserLinkHandlerService extends CoreContentLinksHandlerBas
}
/**
* Check if the handler is enabled for a certain site (site + user) and a URL.
* If not defined, defaults to true.
*
* @param siteId The site ID.
* @param url The URL to treat.
* @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
* @param courseId Course ID related to the URL. Optional but recommended.
* @return Whether the handler is enabled for the URL and site.
* @inheritdoc
*/
async isEnabled(siteId: string, url: string, params: Record<string, string>, courseId?: number): Promise<boolean> {
if (!courseId && !params.id) {

View File

@ -191,7 +191,7 @@ export class CoreH5PContentValidator {
/**
* Validate given value against number semantics.
*
* @param num Number to validate.
* @param value Number to validate.
* @param semantics Semantics.
* @return Validated number.
*/
@ -497,9 +497,7 @@ export class CoreH5PContentValidator {
let validateFunction: undefined | ((...args: unknown[]) => unknown);
let field: CoreH5PSemantics | undefined;
for (let i = 0; i < semantics.fields.length; i++) {
field = semantics.fields[i];
for (const field of semantics.fields) {
if (field.name == key) {
if (semantics.optional) {
field.optional = true;
@ -778,8 +776,7 @@ export class CoreH5PContentValidator {
if (matches && matches.length > 1) {
if (allowedStyles && attrName === 'style') {
// Allow certain styles.
for (let i = 0; i < allowedStyles.length; i++) {
const pattern = allowedStyles[i];
for (const pattern of allowedStyles) {
if (matches[1].match(pattern)) {
// All patterns are start to end patterns, and CKEditor adds one span per style.
attrArray.push('style="' + matches[1] + '"');
@ -1122,7 +1119,7 @@ export class CoreH5PContentValidator {
},
];
return this.metadataSemantics!;
return this.metadataSemantics;
}
/**
@ -1266,7 +1263,7 @@ export class CoreH5PContentValidator {
],
};
return this.copyrightSemantics!;
return this.copyrightSemantics;
}
/**

View File

@ -292,7 +292,7 @@ export class CoreH5PCore {
// Update library usage.
try {
await this.h5pFramework.deleteLibraryUsage(content.id, siteId);
} catch (error) {
} catch {
// Ignore errors.
}

View File

@ -229,7 +229,7 @@ export class CoreH5PFileStorage {
try {
// Delete the index.html.
await this.deleteContentIndex(entry.foldername, site.getId());
} catch (error) {
} catch {
// Ignore errors.
}
});
@ -463,7 +463,7 @@ export class CoreH5PFileStorage {
// Delete existing library version.
try {
await CoreFile.removeDir(folderPath);
} catch (error) {
} catch {
// Ignore errors, maybe it doesn't exist.
}

View File

@ -223,7 +223,7 @@ export class CoreH5PHelper {
// Remove tmp folder.
try {
await CoreFile.removeDir(destFolder);
} catch (error) {
} catch {
// Ignore errors, it will be deleted eventually.
}
}

View File

@ -273,7 +273,7 @@ export class CoreH5PValidator {
const parts = entry.name.split('.'); // The language code is in parts[0].
langIndex[parts[0]] = langFileData;
} catch (error) {
} catch {
// Ignore this language.
}
}));

View File

@ -47,7 +47,7 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
canDownload$ = new BehaviorSubject(false);
calculating$ = new BehaviorSubject(true);
displayOptions?: CoreH5PDisplayOptions;
urlParams?: {[name: string]: string};
urlParams: {[name: string]: string} = {};
protected site: CoreSite;
protected siteId: string;
@ -60,7 +60,7 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
) {
this.logger = CoreLogger.getInstance('CoreH5PPlayerComponent');
this.site = CoreSites.getCurrentSite()!;
this.site = CoreSites.getRequiredCurrentSite();
this.siteId = this.site.getId();
this.siteCanDownload = this.site.canDownloadFiles() && !CoreH5P.isOfflineDisabledInSite();
}
@ -100,7 +100,7 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
// Download the package in background if the size is low.
try {
this.attemptDownloadInBg();
await this.attemptDownloadInBg();
} catch (error) {
this.logger.error('Error downloading H5P in background', error);
}
@ -120,12 +120,12 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
try {
// Get the file size and ask the user to confirm.
const size = await CorePluginFileDelegate.getFileSize({ fileurl: this.urlParams!.url }, this.siteId);
const size = await CorePluginFileDelegate.getFileSize({ fileurl: this.urlParams.url }, this.siteId);
await CoreDomUtils.confirmDownloadSize({ size: size, total: true });
// User confirmed, add to the queue.
await CoreFilepool.addToQueueByUrl(this.siteId, this.urlParams!.url, this.component, this.componentId);
await CoreFilepool.addToQueueByUrl(this.siteId, this.urlParams.url, this.component, this.componentId);
} catch (error) {
if (CoreDomUtils.isCanceledError(error)) {
@ -191,7 +191,6 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
/**
* Calculate state of the file.
*
* @param fileUrl The H5P file URL.
* @return Promise resolved when done.
*/
protected async calculateState(): Promise<void> {
@ -199,7 +198,7 @@ export class CoreH5PPlayerComponent implements OnInit, OnChanges, OnDestroy {
// Get the status of the file.
try {
const state = await CoreFilepool.getFileStateByUrl(this.siteId, this.urlParams!.url);
const state = await CoreFilepool.getFileStateByUrl(this.siteId, this.urlParams.url);
this.canDownload$.next(true);
this.state = state;

View File

@ -14,7 +14,7 @@
<ion-list>
<ion-item class="ion-text-wrap">
<ion-label>
<h2><b>{{ 'core.login.faqcannotfindmysitequestion' | translate }}</b></h2>
<h2><strong>{{ 'core.login.faqcannotfindmysitequestion' | translate }}</strong></h2>
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap">
@ -24,7 +24,7 @@
</ion-item>
<ion-item class="ion-text-wrap">
<ion-label>
<h2><b>{{ 'core.login.faqwhatisurlquestion' | translate }}</b></h2>
<h2><strong>{{ 'core.login.faqwhatisurlquestion' | translate }}</strong></h2>
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap core-login-faqwhatisurlanswer">
@ -34,7 +34,7 @@
</ion-item>
<ion-item class="ion-text-wrap">
<ion-label>
<h2><b>{{ 'core.login.faqcannotconnectquestion' | translate }}</b></h2>
<h2><strong>{{ 'core.login.faqcannotconnectquestion' | translate }}</strong></h2>
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap">
@ -44,7 +44,7 @@
</ion-item>
<ion-item class="ion-text-wrap">
<ion-label>
<h2><b>{{ 'core.login.faqsetupsitequestion' | translate }}</b></h2>
<h2><strong>{{ 'core.login.faqsetupsitequestion' | translate }}</strong></h2>
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap">
@ -57,7 +57,7 @@
</ion-item>
<ion-item class="ion-text-wrap">
<ion-label>
<h2><b>{{ 'core.login.faqtestappquestion' | translate }}</b></h2>
<h2><strong>{{ 'core.login.faqtestappquestion' | translate }}</strong></h2>
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap">
@ -67,7 +67,7 @@
</ion-item>
<ion-item class="ion-text-wrap" *ngIf="canScanQR">
<ion-label>
<h2><b>{{ 'core.login.faqwhereisqrcode' | translate }}</b></h2>
<h2><strong>{{ 'core.login.faqwhereisqrcode' | translate }}</strong></h2>
</ion-label>
</ion-item>
<ion-item class="ion-text-wrap core-login-faqwhereisqrcodeanswer" *ngIf="canScanQR">

View File

@ -360,7 +360,7 @@ export class CoreLoginSitePage implements OnInit {
params: pageParams,
});
}
} catch (error) {
} catch {
// Ignore errors.
}
}
@ -546,7 +546,7 @@ export class CoreLoginSitePage implements OnInit {
},
});
}
} catch (error) {
} catch {
// Ignore errors.
} finally {
modal.dismiss();

View File

@ -625,14 +625,11 @@ export class CorePushNotificationsProvider {
const total = counters.reduce((previous, counter) => previous + counter, 0);
if (!CorePlatform.isMobile()) {
// Browser doesn't have an app badge, stop.
return total;
if (CorePlatform.isMobile()) {
// Set the app badge on mobile.
await Badge.set(total);
}
// Set the app badge.
await Badge.set(total);
return total;
}

View File

@ -404,17 +404,15 @@ export class CoreRatingProvider {
true,
));
const ratingsResults = await Promise.all(promises);
if (!site.isVersionGreaterEqualThan([' 3.6.5', '3.7.1', '3.8'])) {
promises.map((promise) => promise.then(async (ratings) => {
const userIds = ratings.map((rating: CoreRatingItemRating) => rating.userid);
const ratings: CoreRatingItemRating[] = [].concat.apply([], ratingsResults);
await CoreUser.prefetchProfiles(userIds, courseId, site.id);
const userIds = ratings.map((rating) => rating.userid);
return;
}));
await CoreUser.prefetchProfiles(userIds, courseId, site.id);
}
await Promise.all(promises);
}
/**

View File

@ -53,8 +53,9 @@ export class CoreSiteHomeIndexLinkHandlerService extends CoreContentLinksHandler
/**
* @inheritdoc
*/
async isEnabled(siteId: string, url: string, params: Record<string, string>, courseId?: number): Promise<boolean> {
courseId = parseInt(params.id, 10);
async isEnabled(siteId: string, url: string, params: Record<string, string>): Promise<boolean> {
const courseId = parseInt(params.id, 10);
if (!courseId) {
return url.includes('index.php');
}

View File

@ -16,12 +16,12 @@
.contact-status {
width: 24px !important;
height: 24px !important;
right: calc(50% - 12px - var(--core-avatar-size) / 2) !important;
right: calc(50% - 12px - var(--core-avatar-size) / 2) !important;
}
.edit-avatar {
position: absolute;
right: calc(50% - 15px - var(--core-avatar-size) / 2);
right: calc(50% - 15px - var(--core-avatar-size) / 2);
bottom: -12px;
:host-context([dir="rtl"]) & {

View File

@ -16,7 +16,7 @@
.contact-status {
width: 24px !important;
height: 24px !important;
right: calc(50% - 12px - var(--core-avatar-size) / 2) !important;
right: calc(50% - 12px - var(--core-avatar-size) / 2) !important;
}
}
}

View File

@ -30,21 +30,12 @@ export class CoreUserProfileLinkHandlerService extends CoreContentLinksHandlerBa
pattern = /((\/user\/view\.php)|(\/user\/profile\.php)).*([?&]id=\d+)/;
/**
* Get the list of actions for a link (url).
*
* @param siteIds List of sites the URL belongs to.
* @param url The URL to treat.
* @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
* @param courseId Course ID related to the URL. Optional but recommended.
* @param data Extra data to handle the URL.
* @return List of (or promise resolved with list of) actions.
* @inheritdoc
*/
getActions(
siteIds: string[],
url: string,
params: Record<string, string>,
courseId?: number, // eslint-disable-line @typescript-eslint/no-unused-vars
data?: unknown, // eslint-disable-line @typescript-eslint/no-unused-vars
): CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
return [{
action: (siteId): void => {
@ -59,17 +50,9 @@ export class CoreUserProfileLinkHandlerService extends CoreContentLinksHandlerBa
}
/**
* Check if the handler is enabled for a certain site (site + user) and a URL.
* If not defined, defaults to true.
*
* @param siteId The site ID.
* @param url The URL to treat.
* @param params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
* @param courseId Course ID related to the URL. Optional but recommended.
* @return Whether the handler is enabled for the URL and site.
* @inheritdoc
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
async isEnabled(siteId: string, url: string, params: Record<string, string>, courseId?: number): Promise<boolean> {
async isEnabled(siteId: string, url: string): Promise<boolean> {
return url.indexOf('/grade/report/') == -1;
}

View File

@ -181,7 +181,7 @@ export class CoreUserProfileFieldDelegateService extends CoreDelegate<CoreUserPr
if (data) {
result.push(data);
}
} catch (error) {
} catch {
// Ignore errors.
}
}));

View File

@ -43,7 +43,7 @@ export class CoreFormatDatePipe implements PipeTransform {
timestamp = timestamp || Date.now();
format = format || 'strftimedaydatetime';
if (typeof timestamp == 'string') {
if (typeof timestamp === 'string') {
// Convert the value to a number.
const numberTimestamp = parseInt(timestamp, 10);
if (isNaN(numberTimestamp)) {
@ -55,7 +55,7 @@ export class CoreFormatDatePipe implements PipeTransform {
}
// Add "core." if needed.
if (format.indexOf('strf') == 0 || format.indexOf('df') == 0) {
if (format.indexOf('strf') === 0 || format.indexOf('df') === 0) {
format = 'core.' + format;
}

View File

@ -1205,7 +1205,7 @@ export class CoreFileProvider {
});
await Promise.all(promises);
} catch (error) {
} catch {
// Ignore errors, maybe it doesn't exist.
}
}

View File

@ -1467,7 +1467,7 @@ export class CoreFilepoolProvider {
const fileSize = await CoreFile.getFileSize(file.path);
size += fileSize;
} catch (error) {
} catch {
// Ignore failures, maybe some file was deleted.
}
}));

View File

@ -200,10 +200,8 @@ export class CorePluginFileDelegateService extends CoreDelegate<CorePluginFileHa
if (handler && handler.getFileSize) {
try {
const size = handler.getFileSize(downloadableFile, siteId);
return size;
} catch (error) {
return await handler.getFileSize(downloadableFile, siteId);
} catch {
// Ignore errors.
}
}

View File

@ -302,14 +302,12 @@ export class CoreMimetypeUtilsProvider {
*/
guessExtensionFromUrl(fileUrl: string): string | undefined {
const split = fileUrl.split('.');
let candidate;
let extension;
let position;
let extension: string | undefined;
if (split.length > 1) {
candidate = split[split.length - 1].toLowerCase();
let candidate = split[split.length - 1].toLowerCase();
// Remove params if any.
position = candidate.indexOf('?');
let position = candidate.indexOf('?');
if (position > -1) {
candidate = candidate.substring(0, position);
}
@ -343,7 +341,7 @@ export class CoreMimetypeUtilsProvider {
*/
getFileExtension(filename: string): string | undefined {
const dot = filename.lastIndexOf('.');
let ext;
let ext: string | undefined;
if (dot > -1) {
ext = filename.substring(dot + 1).toLowerCase();
@ -582,11 +580,10 @@ export class CoreMimetypeUtilsProvider {
*/
removeExtension(path: string): string {
const position = path.lastIndexOf('.');
let extension;
if (position > -1) {
// Check extension corresponds to a mimetype to know if it's valid.
extension = path.substring(position + 1).toLowerCase();
const extension = path.substring(position + 1).toLowerCase();
if (this.getMimeType(extension) !== undefined) {
return path.substring(0, position); // Remove extension.
}

View File

@ -741,7 +741,7 @@ export class CoreTextUtilsProvider {
* @returns Treated text.
*/
replaceArguments(text: string, replacements: Record<string, string> = {}, encoding?: 'uri'): string {
let match;
let match: RegExpMatchArray | null = null;
while ((match = text.match(/\{\{([^}]+)\}\}/))) {
const argument = match[1].trim();
@ -829,7 +829,7 @@ export class CoreTextUtilsProvider {
/**
* Replace @@PLUGINFILE@@ wildcards with the real URL in a text.
*
* @param Text to treat.
* @param text to treat.
* @param files Files to extract the pluginfile URL from. They need to have the URL in a url or fileurl attribute.
* @return Treated text.
*/
@ -847,8 +847,10 @@ export class CoreTextUtilsProvider {
/**
* Restore original draftfile URLs.
*
* @param text Text to treat, including pluginfile URLs.
* @param replaceMap Map of the replacements that were done.
* @param siteUrl Site URL.
* @param treatedText Treated text with replacements.
* @param originalText Original text.
* @param files List of files to search and replace.
* @return Treated text.
*/
restoreDraftfileUrls(siteUrl: string, treatedText: string, originalText: string, files: CoreWSFile[]): string {
@ -1053,7 +1055,6 @@ export class CoreTextUtilsProvider {
*
* @param title Title of the new state.
* @param content Content of the text to be expanded.
* @param component Component to link the embedded files to.
* @param options Options.
* @return Promise resolved when the modal is displayed.
*/

View File

@ -170,7 +170,7 @@ export class CoreTimeUtilsProvider {
/**
* Converts a number of seconds into a short human readable format: minutes and seconds, in fromat: 3' 27''.
*
* @param seconds Seconds
* @param duration Seconds
* @return Short human readable text.
* @deprecated since app 4.0. Use CoreTime.formatTimeShort instead.
*/

View File

@ -544,7 +544,7 @@ export class CoreUrlUtilsProvider {
url = url.replace(/\/webservice\/pluginfile\.php\//, '/pluginfile.php/');
// Make sure the URL doesn't contain the token.
url.replace(/([?&])token=[^&]*&?/, '$1');
url = url.replace(/([?&])token=[^&]*&?/, '$1');
return url;
}

View File

@ -37,7 +37,7 @@ import { CoreColors } from '@singletons/colors';
import { CorePromisedValue } from '@classes/promised-value';
import { CorePlatform } from '@services/platform';
type TreeNode<T> = T & { children: TreeNode<T>[] };
export type TreeNode<T> = T & { children: TreeNode<T>[] };
/*
* "Utils" service with helper functions.
@ -486,7 +486,7 @@ export class CoreUtilsProvider {
* @param ...args All the params sent after checkAll will be passed to isEnabledFn.
* @return Promise resolved with the list of enabled sites.
*/
filterEnabledSites<P extends unknown[]>(
async filterEnabledSites<P extends unknown[]>(
siteIds: string[],
isEnabledFn: (siteId, ...args: P) => boolean | Promise<boolean>,
checkAll?: boolean,
@ -507,16 +507,14 @@ export class CoreUtilsProvider {
}
}
return this.allPromises(promises).catch(() => {
// Ignore errors.
}).then(() => {
if (!checkAll) {
// Checking 1 was enough, so it will either return all the sites or none.
return enabledSites.length ? siteIds : [];
} else {
return enabledSites;
}
});
await CoreUtils.ignoreErrors(this.allPromises(promises));
if (!checkAll) {
// Checking 1 was enough, so it will either return all the sites or none.
return enabledSites.length ? siteIds : [];
} else {
return enabledSites;
}
}
/**
@ -562,18 +560,30 @@ export class CoreUtilsProvider {
const mapDepth = {};
const tree: TreeNode<T>[] = [];
// Create a map first to avoid problems with not sorted.
list.forEach((node: TreeNode<T>, index): void => {
const id = node[idFieldName];
const parent = node[parentFieldName];
node.children = [];
if (!id || !parent) {
if (id === undefined) {
this.logger.error(`Node with incorrect ${idFieldName}:${id} found on formatTree`);
}
if (node.children === undefined) {
node.children = [];
}
map[id] = index;
});
list.forEach((node: TreeNode<T>): void => {
const id = node[idFieldName];
const parent = node[parentFieldName];
if (id === undefined || parent === undefined) {
this.logger.error(`Node with incorrect ${idFieldName}:${id} or ${parentFieldName}:${parent} found on formatTree`);
}
// Use map to look-up the parents.
map[id] = index;
if (parent != rootParentId) {
if (parent !== rootParentId) {
const parentNode = list[map[parent]] as TreeNode<T>;
if (parentNode) {
if (mapDepth[parent] == maxDepth) {
@ -627,21 +637,21 @@ export class CoreUtilsProvider {
*
* @return Promise resolved with the list of countries.
*/
getCountryList(): Promise<Record<string, string>> {
async getCountryList(): Promise<Record<string, string>> {
// Get the keys of the countries.
return this.getCountryKeysList().then((keys) => {
// Now get the code and the translated name.
const countries = {};
const keys = await this.getCountryKeysList();
keys.forEach((key) => {
if (key.indexOf('assets.countries.') === 0) {
const code = key.replace('assets.countries.', '');
countries[code] = Translate.instant(key);
}
});
// Now get the code and the translated name.
const countries: Record<string, string> = {};
return countries;
keys.forEach((key) => {
if (key.indexOf('assets.countries.') === 0) {
const code = key.replace('assets.countries.', '');
countries[code] = Translate.instant(key);
}
});
return countries;
}
/**
@ -649,18 +659,14 @@ export class CoreUtilsProvider {
*
* @return Promise resolved with the list of countries.
*/
getCountryListSorted(): Promise<CoreCountry[]> {
async getCountryListSorted(): Promise<CoreCountry[]> {
// Get the keys of the countries.
return this.getCountryList().then((countries) => {
// Sort translations.
const sortedCountries: { code: string; name: string }[] = [];
const countries = await this.getCountryList();
Object.keys(countries).sort((a, b) => countries[a].localeCompare(countries[b])).forEach((key) => {
sortedCountries.push({ code: key, name: countries[key] });
});
return sortedCountries;
});
// Sort translations.
return Object.keys(countries)
.sort((a, b) => countries[a].localeCompare(countries[b]))
.map((code) => ({ code, name: countries[code] }));
}
/**
@ -668,11 +674,13 @@ export class CoreUtilsProvider {
*
* @return Promise resolved with the countries list. Rejected if not translated.
*/
protected getCountryKeysList(): Promise<string[]> {
protected async getCountryKeysList(): Promise<string[]> {
// It's possible that the current language isn't translated, so try with default language first.
const defaultLang = CoreLang.getDefaultLanguage();
return this.getCountryKeysListForLanguage(defaultLang).catch(() => {
try {
return await this.getCountryKeysListForLanguage(defaultLang);
} catch {
// Not translated, try to use the fallback language.
const fallbackLang = CoreLang.getFallbackLanguage();
@ -681,8 +689,8 @@ export class CoreUtilsProvider {
throw new Error('Countries not found.');
}
return this.getCountryKeysListForLanguage(fallbackLang);
});
return await this.getCountryKeysListForLanguage(fallbackLang);
}
}
/**
@ -720,17 +728,19 @@ export class CoreUtilsProvider {
* @param url The URL of the file.
* @return Promise resolved with the mimetype.
*/
getMimeTypeFromUrl(url: string): Promise<string> {
async getMimeTypeFromUrl(url: string): Promise<string> {
// First check if it can be guessed from the URL.
const extension = CoreMimetypeUtils.guessExtensionFromUrl(url);
const mimetype = extension && CoreMimetypeUtils.getMimeType(extension);
let mimetype = extension && CoreMimetypeUtils.getMimeType(extension);
if (mimetype) {
return Promise.resolve(mimetype);
return mimetype;
}
// Can't be guessed, get the remote mimetype.
return CoreWS.getRemoteFileMimeType(url).then(mimetype => mimetype || '');
mimetype = await CoreWS.getRemoteFileMimeType(url);
return mimetype || '';
}
/**
@ -760,7 +770,7 @@ export class CoreUtilsProvider {
/**
* Check if an unknown value is a FileEntry.
*
* @param value Value to check.
* @param file Object to check.
* @return Type guard indicating if the file is a FileEntry.
*/
valueIsFileEntry(file: unknown): file is FileEntry {
@ -1017,7 +1027,7 @@ export class CoreUtilsProvider {
this.iabInstance = InAppBrowser.create(url, '_blank', options);
if (CorePlatform.isMobile()) {
let loadStopSubscription;
let loadStopSubscription: Subscription | undefined;
const loadStartUrls: string[] = [];
// Trigger global events when a url is loaded or the window is closed. This is to make it work like in Ionic 1.
@ -1708,7 +1718,7 @@ export class CoreUtilsProvider {
* Ignore errors from a promise.
*
* @param promise Promise to ignore errors.
* @param fallbackResult Value to return if the promise is rejected.
* @param fallback Value to return if the promise is rejected.
* @return Promise with ignored errors, resolving to the fallback result if provided.
*/
async ignoreErrors<Result>(promise: Promise<Result>): Promise<Result | undefined>;