Merge pull request #3850 from crazyserver/MOBILE-4465

Mobile 4465
main
Dani Palou 2023-11-20 11:04:33 +01:00 committed by GitHub
commit 07f661e5ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 27 additions and 1633 deletions

View File

@ -29,7 +29,7 @@
<ion-item class="ion-text-wrap" *ngIf="activityInstructions">
<ion-label>
<core-format-text [text]="activityInstructions" [component]="component" [componentId]="moduleId" contextLevel="module"
[contextInstanceId]="moduleId" [courseId]="courseId" [maxHeight]="120">
[contextInstanceId]="moduleId" [courseId]="courseId">
</core-format-text>
</ion-label>
</ion-item>

View File

@ -19,10 +19,6 @@ import { CoreSiteError, CoreSiteErrorOptions } from '@classes/errors/siteerror';
*/
export class CoreAjaxError extends CoreSiteError {
/**
* @deprecated since 4.0. AJAX endpoint should always be available in supported Moodle versions.
*/
available = 1;
status?: number;
constructor(messageOrOptions: string | CoreSiteErrorOptions, available?: number, status?: number) {

View File

@ -1634,38 +1634,6 @@ export class CoreSite {
return CoreUrlUtils.addParamsToUrl(CorePath.concatenatePaths(this.siteUrl, path), params, anchor);
}
/**
* Check if the local_mobile plugin is installed in the Moodle site.
*
* @returns Promise resolved when the check is done.
* @deprecated since 4.0.
*/
// eslint-disable-next-line deprecation/deprecation
async checkLocalMobilePlugin(): Promise<LocalMobileResponse> {
// Not used anymore.
return { code: 0, coreSupported: true };
}
/**
* Check if local_mobile has been installed in Moodle.
*
* @returns Whether the App is able to use local_mobile plugin for this site.
* @deprecated since 4.0.
*/
checkIfAppUsesLocalMobile(): boolean {
return false;
}
/**
* Check if local_mobile has been installed in Moodle but the app is not using it.
*
* @returns Promise resolved it local_mobile was added, rejected otherwise.
* @deprecated since 4.0.
*/
async checkIfLocalMobileInstalledAndNotUsed(): Promise<void> {
throw new CoreError('Deprecated.');
}
/**
* Check if a URL belongs to this site.
*
@ -2666,33 +2634,6 @@ export type CoreSiteWSPreSets = {
updateInBackground?: boolean;
};
/**
* Response of checking local_mobile status.
*
* @deprecated since 4.0.
*/
export type LocalMobileResponse = {
/**
* Code to identify the authentication method to use.
*/
code: number;
/**
* Name of the service to use.
*/
service?: string;
/**
* Code of the warning message.
*/
warning?: string;
/**
* Whether core SSO is supported.
*/
coreSupported?: boolean;
};
/**
* Info of a request waiting in the queue.
*/

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { Component, Input, Output, OnInit, OnDestroy, EventEmitter, OnChanges, SimpleChange } from '@angular/core';
import { CoreLogger } from '@singletons/logger';
import { CoreContextMenuComponent } from '../context-menu/context-menu';
/**
@ -58,11 +57,6 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange
@Output() onClosed?: EventEmitter<() => void>; // Will emit an event when the popover is closed because the item was clicked.
@Output() toggleChange = new EventEmitter<boolean>();// Will emit an event when toggle changes to enable 2-way data binding.
/**
* @deprecated since 4.0. Not used anymore.
*/
@Input() iconDescription?: string;
protected hasAction = false;
protected destroyed = false;
@ -92,12 +86,6 @@ export class CoreContextMenuItemComponent implements OnInit, OnDestroy, OnChange
if (!this.destroyed) {
this.ctxtMenu.addItem(this);
}
// eslint-disable-next-line deprecation/deprecation
if (this.iconDescription !== undefined) {
CoreLogger.getInstance('CoreContextMenuItemComponent')
.warn('iconDescription Input is deprecated and should not be used');
}
}
/**

View File

@ -35,10 +35,6 @@ export class CoreDownloadRefreshComponent {
@Input() statusTranslatable?: string; // Download status translatable string.
@Input() enabled = false; // Whether the download is enabled.
@Input() loading = true; // Force loading status when is not downloading.
/**
* @deprecated since 4.0. Not used anymore.
*/
@Input() size = ''; // Size of the buttons.
@Input() canTrustDownload = false; // If false, refresh will be shown if downloaded.
@Output() action: EventEmitter<boolean>; // Will emit an event when the item clicked.

View File

@ -35,11 +35,6 @@ export class CoreEmptyBoxComponent {
@Input() image?: string; // Image source. If an icon is provided, image won't be used.
@Input() flipIconRtl = false; // Whether to flip the icon in RTL. Defaults to false.
/**
* @deprecated since 4.0. Not used anymore.
*/
@Input() inline = false;
@HostBinding('class.dimmed')
get isDimmed(): boolean {
return this.dimmed;

View File

@ -14,7 +14,6 @@
import { Component, Input, OnInit, OnChanges, SimpleChange, ElementRef, AfterViewInit, OnDestroy } from '@angular/core';
import { CoreEventLoadingChangedData, CoreEvents } from '@singletons/events';
import { CoreUtils } from '@services/utils/utils';
import { CoreAnimations } from '@components/animations';
import { Translate } from '@singletons';
@ -153,13 +152,6 @@ export class CoreLoadingComponent implements OnInit, OnChanges, AfterViewInit, A
this.lastScrollPosition = this.getScrollPosition();
this.mutationObserver.disconnect();
}
// Event has been deprecated since app 4.0.
// eslint-disable-next-line deprecation/deprecation
CoreEvents.trigger(CoreEvents.CORE_LOADING_CHANGED, <CoreEventLoadingChangedData> {
loaded,
uniqueId: this.uniqueId,
});
}
/**

View File

@ -78,10 +78,6 @@ export class CoreConstants {
// WS constants.
static readonly WS_TIMEOUT = 30000; // Timeout when not in WiFi.
static readonly WS_TIMEOUT_WIFI = 30000; // Timeout when in WiFi.
/**
* @deprecated since 4.0. Not used anymore.
*/
static readonly WS_PREFIX = 'local_mobile_';
// Login constants.
/**

View File

@ -90,21 +90,6 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
@Input() hideIfEmpty = false; // If true, the tag will contain nothing if text is empty.
@Input() disabled?: boolean; // If disabled, autoplay elements will be disabled.
/**
* @deprecated since 4.0. Not used anymore.
*/
@Input() fullOnClick?: boolean | string;
/**
* @deprecated since 4.0. Not used anymore.
*/
@Input() fullTitle?: string;
/**
* Max height in pixels to render the content box. It should be 50 at least to make sense.
*
* @deprecated since 4.0 Use collapsible-item directive instead.
*/
@Input() maxHeight?: number;
@Output() afterRender: EventEmitter<void>; // Called when the data is rendered.
@Output() onClick: EventEmitter<void> = new EventEmitter(); // Called when clicked.
@ -380,15 +365,6 @@ export class CoreFormatTextDirective implements OnChanges, OnDestroy, AsyncDirec
await CoreUtils.nextTick();
// Use collapsible-item directive instead.
// eslint-disable-next-line deprecation/deprecation
if (this.maxHeight && !this.collapsible) {
this.collapsible = new CoreCollapsibleItemDirective(new ElementRef(this.element));
// eslint-disable-next-line deprecation/deprecation
this.collapsible.height = this.maxHeight;
this.collapsible.ngOnInit();
}
// Add magnifying glasses to images.
this.addMagnifyingGlasses();

View File

@ -13,7 +13,6 @@
// limitations under the License.
import { CoreContentLinksHandlerBase } from './base-handler';
import { Params } from '@angular/router';
import { CoreContentLinksAction } from '../services/contentlinks-delegate';
import { CoreCourseHelper } from '@features/course/services/course-helper';
import { CoreNavigationOptions } from '@services/navigator';
@ -52,20 +51,6 @@ export class CoreContentLinksModuleIndexHandler extends CoreContentLinksHandlerB
this.featureName = 'CoreCourseModuleDelegate_' + addon;
}
/**
* Get the mod params necessary to open an activity.
*
* @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.
* @returns List of params to pass to navigateToModule / navigateToModuleByInstance.
* @deprecated since 4.0. Not used anymore.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getPageParams(url: string, params: Record<string, string>, courseId?: number): Params {
return {};
}
/**
* Get the navigation options to open the module.
*
@ -97,19 +82,6 @@ export class CoreContentLinksModuleIndexHandler extends CoreContentLinksHandlerB
): CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
courseId = Number(courseId || params.courseid || params.cid);
const getModNavOptions = (siteId: string): CoreNavigationOptions => {
let modNavOptions = this.getModNavOptions(url, params, siteId, courseId);
if (!modNavOptions) {
// Use the old function, currently deprecated.
// eslint-disable-next-line deprecation/deprecation
const pageParams = this.getPageParams(url, params, courseId);
if (pageParams && Object.keys(pageParams).length > 0) {
modNavOptions = { params: pageParams };
}
}
return modNavOptions;
};
if (this.instanceIdParam && params[this.instanceIdParam] !== undefined) {
const instanceId = parseInt(params[this.instanceIdParam], 10);
@ -122,7 +94,7 @@ export class CoreContentLinksModuleIndexHandler extends CoreContentLinksHandlerB
{
courseId,
useModNameToGetModule: this.useModNameToGetModule,
modNavOptions: getModNavOptions(siteId),
modNavOptions: this.getModNavOptions(url, params, siteId, courseId),
siteId,
},
);
@ -137,7 +109,7 @@ export class CoreContentLinksModuleIndexHandler extends CoreContentLinksHandlerB
{
courseId,
modName: this.useModNameToGetModule ? this.modName : undefined,
modNavOptions: getModNavOptions(siteId),
modNavOptions: this.getModNavOptions(url, params, siteId, courseId),
siteId,
},
);

View File

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input } from '@angular/core';
import { Component, HostBinding, Input } from '@angular/core';
/**
* Component to display the description of a module.
@ -47,4 +47,8 @@ export class CoreCourseModuleDescriptionComponent {
@Input() contextInstanceId?: number; // The instance ID related to the context.
@Input() courseId?: number; // Course ID the text belongs to. It can be used to improve performance with filters.
@HostBinding('class.deprecated') get isDeprecated(): boolean {
return true;
};
}

View File

@ -148,14 +148,6 @@ export class CoreCourseIndexPage implements OnInit, OnDestroy {
this.modNavOptions = CoreNavigator.getRouteParam<CoreNavigationOptions>('modNavOptions');
this.openModule = CoreNavigator.getRouteBooleanParam('openModule') ?? true; // If false, just scroll to module.
if (!this.modNavOptions) {
// Fallback to old way of passing params. @deprecated since 4.0.
const modParams = CoreNavigator.getRouteParam<Params>('modParams');
if (modParams) {
this.modNavOptions = { params: modParams };
}
}
this.currentPagePath = CoreNavigator.getCurrentPath();
this.contentsTab.page = CorePath.concatenatePaths(this.currentPagePath, this.contentsTab.page);
this.contentsTab.pageParams = {

View File

@ -42,7 +42,6 @@ import {
CoreEnrolledCourseData,
} from '@features/courses/services/courses';
import { CoreArray } from '@singletons/array';
import { CoreIonLoadingElement } from '@classes/ion-loading';
import { CoreCourseOffline } from './course-offline';
import {
CoreCourseOptionsDelegate,
@ -258,22 +257,6 @@ export class CoreCourseHelperProvider {
return section.uservisible !== false;
}
/**
* Calculate completion data of a module.
*
* @param module Module.
* @deprecated since 4.0.
*/
calculateModuleCompletionData(module: CoreCourseModuleData): void {
if (!module.completiondata || !module.completion) {
return;
}
module.completiondata.courseId = module.course;
module.completiondata.tracking = module.completion;
module.completiondata.cmid = module.id;
}
/**
* Calculate the status of a section.
*
@ -312,8 +295,6 @@ export class CoreCourseHelperProvider {
}
sectionWithStatus.downloadStatus = result.status;
// eslint-disable-next-line deprecation/deprecation
sectionWithStatus.canCheckUpdates = true;
// Set this section data.
if (result.status !== CoreConstants.DOWNLOADING) {
@ -375,8 +356,6 @@ export class CoreCourseHelperProvider {
if (allSectionsSection) {
// Set "All sections" data.
allSectionsSection.downloadStatus = allSectionsStatus;
// eslint-disable-next-line deprecation/deprecation
allSectionsSection.canCheckUpdates = true;
allSectionsSection.isDownloading = allSectionsStatus === CoreConstants.DOWNLOADING;
}
@ -514,32 +493,6 @@ export class CoreCourseHelperProvider {
return CoreUtils.allPromises(promises);
}
/**
* Show confirmation dialog and then remove a module files.
*
* @param module Module to remove the files.
* @param courseId Course ID the module belongs to.
* @returns Promise resolved when done.
* @deprecated since 4.0.
*/
async confirmAndRemoveFiles(module: CoreCourseModuleData, courseId: number): Promise<void> {
let modal: CoreIonLoadingElement | undefined;
try {
await CoreDomUtils.showDeleteConfirm('addon.storagemanager.confirmdeletedatafrom', { name: module.name });
modal = await CoreDomUtils.showModalLoading();
await this.removeModuleStoredData(module, courseId);
} catch (error) {
if (error) {
CoreDomUtils.showErrorModal(error);
}
} finally {
modal?.dismiss();
}
}
/**
* Calculate the size to download a section and show a confirm modal if needed.
*
@ -1358,31 +1311,6 @@ export class CoreCourseHelperProvider {
return CoreConstants.ICON_DOWNLOADING;
}
/**
* Get the course ID from a module instance ID, showing an error message if it can't be retrieved.
*
* @param instanceId Instance ID.
* @param moduleName Name of the module. E.g. 'glossary'.
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved with the module's course ID.
* @deprecated since 4.0.
*/
async getModuleCourseIdByInstance(instanceId: number, moduleName: string, siteId?: string): Promise<number> {
try {
const cm = await CoreCourse.getModuleBasicInfoByInstance(
instanceId,
moduleName,
{ siteId, readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE },
);
return cm.course;
} catch (error) {
CoreDomUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
throw error;
}
}
/**
* Get prefetch info for a module.
*
@ -1805,8 +1733,6 @@ export class CoreCourseHelperProvider {
// Set "All sections" data.
section.downloadStatus = allSectionsStatus;
// eslint-disable-next-line deprecation/deprecation
section.canCheckUpdates = true;
section.isDownloading = allSectionsStatus === CoreConstants.DOWNLOADING;
} finally {
section.isDownloading = false;
@ -2120,10 +2046,6 @@ export type CoreCourseSection = CoreCourseWSSection & {
*/
export type CoreCourseSectionWithStatus = CoreCourseSection & {
downloadStatus?: string; // Section status.
/**
* @deprecated since 4.0.
*/
canCheckUpdates?: boolean; // Whether can check updates.
isDownloading?: boolean; // Whether section is being downloaded.
total?: number; // Total of modules being downloaded.
count?: number; // Number of downloaded modules.
@ -2141,13 +2063,6 @@ export type CoreCourseModuleData = Omit<CoreCourseGetContentsWSModule, 'completi
section: number;
};
/**
* Module with calculated data.
*
* @deprecated since 4.0. Use CoreCourseModuleData instead.
*/
export type CoreCourseModule = CoreCourseModuleData;
/**
* Module completion with calculated data.
*/

View File

@ -110,36 +110,6 @@ export class CoreCourseProvider {
static readonly ACCESS_DEFAULT = 'courses_access_default';
static readonly ALL_COURSES_CLEARED = -1;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE instead.
*/
static readonly COMPLETION_TRACKING_NONE = 0;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL instead.
*/
static readonly COMPLETION_TRACKING_MANUAL = 1;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC instead.
*/
static readonly COMPLETION_TRACKING_AUTOMATIC = 2;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE instead.
*/
static readonly COMPLETION_INCOMPLETE = 0;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE instead.
*/
static readonly COMPLETION_COMPLETE = 1;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS instead.
*/
static readonly COMPLETION_COMPLETE_PASS = 2;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL instead.
*/
static readonly COMPLETION_COMPLETE_FAIL = 3;
static readonly COMPONENT = 'CoreCourse';
readonly CORE_MODULES = [
@ -863,24 +833,6 @@ export class CoreCourseProvider {
return path + moduleName + '.svg';
}
/**
* Get the section ID a module belongs to.
*
* @param moduleId The module ID.
* @param siteId Site ID. If not defined, current site.
* @returns Promise resolved with the section ID.
* @deprecated since 4.0.
*/
async getModuleSectionId(moduleId: number, siteId?: string): Promise<number> {
// Try to get the section using getModuleBasicInfo.
const module = await CoreCourse.getModuleBasicInfo(
moduleId,
{ siteId, readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE },
);
return module.section;
}
/**
* Return a specific section.
*
@ -1829,15 +1781,6 @@ type CoreCourseGetCourseModuleWSResponse = {
warnings?: CoreWSExternalWarning[];
};
/**
* Course module data returned by the WS with course added.
*
* @deprecated since 4.0. Use CoreCourseModuleData instead.
*/
export type CoreCourseWSModule = CoreCourseGetContentsWSModule & {
course: number; // The course id.
};
/**
* Module completion data.
*/

View File

@ -57,24 +57,6 @@ export interface CoreCourseFormatHandler extends CoreDelegateHandler {
*/
displayBlocks?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the option to enable section/module download should be displayed.
*
* @param course The course to check.
* @returns Whether the option to enable section/module download should be displayed.
* @deprecated since 4.0 Not used anymore because prefetch has been moved to storage manager.
*/
displayEnableDownload?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the default course index should be displayed. Defaults to true.
*
* @param course The course to check.
* @returns Whether the default course index should be displayed.
* @deprecated since 4.0. Please use displayCourseIndex instead.
*/
displaySectionSelector?(course: CoreCourseAnyCourseData): boolean;
/**
* Whether the default section selector should be displayed. Defaults to true.
*

View File

@ -77,16 +77,6 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate<CoreCo
}, CoreSites.getCurrentSiteId());
}
/**
* Check if current site can check updates using core_course_check_updates.
*
* @returns True if can check updates, false otherwise.
* @deprecated since 4.0.
*/
canCheckUpdates(): boolean {
return true;
}
/**
* Check if a certain module can use core_course_check_updates.
*
@ -705,8 +695,7 @@ export class CoreCourseModulePrefetchDelegateService extends CoreDelegate<CoreCo
await Promise.all(modules.map(async (module) => {
const handler = this.getPrefetchHandlerFor(module.modname);
// eslint-disable-next-line deprecation/deprecation
if (!handler || (onlyToDisplay && handler.skipListStatus)) {
if (!handler) {
return;
}
@ -1364,14 +1353,6 @@ export interface CoreCourseModulePrefetchHandler extends CoreDelegateHandler {
*/
updatesNames?: RegExp;
/**
* If true, this module will be treated as not downloadable when determining the status of a list of modules. The module will
* still be downloaded when downloading the section/course, it only affects whether the button should be displayed.
*
* @deprecated since 4.0.
*/
skipListStatus?: boolean;
/**
* Get the download size of a module.
*

View File

@ -1,60 +1,3 @@
<ion-card [attr.course-color]="course.color ? null : course.colorNumber">
<div (click)="openCourse()" class="core-course-thumb" [class.core-course-color-img]="course.courseimage"
[style.background-color]="course.color">
<img *ngIf="course.courseimage" [src]="course.courseimage" core-external-content alt="" />
</div>
<ion-item button (click)="openCourse()" [attr.aria-label]="course.displayname || course.fullname" class="core-course-header"
[class.core-course-only-title]="!showAll || progress < 0 && completionUserTracked === false" detail="false">
<ion-label class="ion-text-wrap core-course-title"
[class.core-course-with-buttons]="courseOptionMenuEnabled || (downloadCourseEnabled && showDownload)"
[class.core-course-with-spinner]="(downloadCourseEnabled && prefetchCourseData.icon == 'spinner') || showSpinner">
<p *ngIf="course.categoryname || (course.displayname && course.shortname && course.fullname != course.displayname)"
class="core-course-additional-info">
<span class="sr-only">{{ 'core.courses.aria:coursecategory' | translate }}</span>
<span *ngIf="course.categoryname" class="core-course-category">
<core-format-text [text]="course.categoryname"></core-format-text>
</span>
<span *ngIf="course.categoryname && course.displayname && course.shortname && course.fullname != course.displayname"
class="core-course-category"> | </span>
<span *ngIf="course.displayname && course.shortname && course.fullname != course.displayname" class="core-course-shortname">
<core-format-text [text]="course.shortname" contextLevel="course" [contextInstanceId]="course.id">
</core-format-text>
</span>
</p>
<p class="item-heading">
<ion-icon name="fas-star" *ngIf="isFavourite" [attr.aria-label]="'core.courses.favourite' | translate">
</ion-icon>
<span class="sr-only" *ngIf="isFavourite">{{ 'core.courses.aria:favourite' | translate }}</span>
<span class="sr-only">{{ 'core.courses.aria:coursename' | translate }}</span>
<core-format-text [text]="course.fullname" contextLevel="course" [contextInstanceId]="course.id"></core-format-text>
</p>
</ion-label>
<div class="core-button-spinner" *ngIf="downloadCourseEnabled && !courseOptionMenuEnabled && showDownload" slot="end">
<core-download-refresh [status]="prefetchCourseData.status" [enabled]="downloadCourseEnabled"
[statusTranslatable]="prefetchCourseData.statusTranslatable" [canTrustDownload]="false"
[loading]="prefetchCourseData.loading" (action)="prefetchCourse()"></core-download-refresh>
</div>
<div class="core-button-spinner" *ngIf="courseOptionMenuEnabled" slot="end">
<!-- Download course spinner. -->
<ion-spinner *ngIf="(downloadCourseEnabled && prefetchCourseData.icon == 'spinner') || showSpinner"
[attr.aria-label]="'core.loading' | translate"></ion-spinner>
<!-- Downloaded icon. -->
<ion-icon *ngIf="downloadCourseEnabled && prefetchCourseData.downloadSucceeded && !showSpinner" class="core-icon-downloaded"
name="fam-cloud-done" color="success" role="status" [attr.aria-label]="'core.downloaded' | translate"></ion-icon>
<!-- Options menu. -->
<ion-button fill="clear" (click)="showCourseOptionsMenu($event)" *ngIf="!showSpinner"
[attr.aria-label]="('core.displayoptions' | translate)">
<ion-icon name="ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</div>
</ion-item>
<ion-item *ngIf="showAll && progress >= 0 && completionUserTracked !== false" class="core-course-progress">
<ion-label>
<core-progress-bar [progress]="progress" a11yText="core.courses.aria:courseprogress"></core-progress-bar>
</ion-label>
</ion-item>
</ion-card>
<core-courses-course-list-item [course]="course" class="core-course-progress" [showDownload]="showDownload"
[layout]="showAll ? 'card' : 'summarycard'">
</core-courses-course-list-item>

View File

@ -1,159 +0,0 @@
@import "~theme/globals";
:host {
ion-card {
--vertical-margin: 12px;
display: flex;
flex-direction: column;
align-self: stretch;
height: calc(100% - var(--vertical-margin) - var(--vertical-margin));
margin-top: var(--vertical-margin);
margin-bottom: var(--vertical-margin);
@for $i from 0 to length($core-course-image-background) {
&[course-color="#{$i}"] .core-course-thumb {
background: var(--core-course-color-#{$i});
}
}
.core-course-thumb {
padding-top: 40%;
width: 100%;
overflow: hidden;
cursor: pointer;
pointer-events: auto;
position: relative;
background-position: center;
background-size: cover;
-webkit-transition: all 50ms ease-in-out;
transition: all 50ms ease-in-out;
&.core-course-color-img {
background: var(--ion-item-background);
}
img {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 0;
margin: auto;
// Fill geopaterns
&[src$=".svg"] {
min-width: 100%;
}
}
}
@if ($core-course-hide-thumb-on-cards) {
.core-course-thumb {
display: none;
}
}
@if ($core-course-thumb-on-cards-background) {
.core-course-thumb {
background: $core-course-thumb-on-cards-background !important;
}
}
.core-course-additional-info {
margin-bottom: 8px;
}
.core-course-header {
flex-grow: 1;
display: flex;
flex-direction: column;
&.core-course-only-title {
&::part(native) {
flex-grow: 1;
}
}
.core-course-title {
margin: 12px 0;
flex-grow: 1;
.item-heading ion-icon {
margin-right: 4px;
color: var(--core-star-color);
}
}
.core-button-spinner {
margin: 0;
}
.core-button-spinner ion-spinner {
vertical-align: top; // the better option for most scenarios
vertical-align: -webkit-baseline-middle; // the best for those that support it
}
.core-button-spinner .core-icon-downloaded {
font-size: 28.8px;
margin-top: 8px;
vertical-align: top;
}
.item-button[icon-only] {
min-width: 50px;
width: 50px;
}
}
@if ($core-course-hide-progress-on-cards) {
.core-course-progress {
display: none;
}
}
}
button {
z-index: 1;
}
}
// Common styles.
:host-context(.core-horizontal-scroll) {
@include horizontal_scroll_item(80%, 250px, 300px);
ion-card {
.core-course-thumb {
padding-top: 30%;
}
.core-course-header {
.core-course-title {
margin: 7px 0;
.item-heading ion-icon {
margin-right: 2px;
}
&.core-course-with-buttons {
max-width: calc(100% - 40px);
}
}
.core-button-spinner {
min-height: 40px;
min-width: 40px;
ion-spinner {
width: 20px;
height: 20px;
}
}
.item-button[icon-only] {
min-width: 40px;
width: 40px;
padding: 8px;
}
}
}
}

View File

@ -12,21 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, Input, OnInit, OnDestroy, OnChanges } from '@angular/core';
import { CoreEventCourseStatusChanged, CoreEventObserver, CoreEvents } from '@singletons/events';
import { CoreSites } from '@services/sites';
import { CoreDomUtils } from '@services/utils/dom';
import { CoreCourses, CoreCoursesProvider } from '@features/courses/services/courses';
import { CoreCourse, CoreCourseProvider } from '@features/course/services/course';
import { CoreCourseHelper, CorePrefetchStatusInfo } from '@features/course/services/course-helper';
import { Translate } from '@singletons';
import { CoreConstants } from '@/core/constants';
import {
CoreCourseAnyCourseDataWithExtraInfoAndOptions,
CoreEnrolledCourseDataWithExtraInfoAndOptions,
} from '../../services/courses-helper';
import { CoreCoursesCourseOptionsMenuComponent } from '../course-options-menu/course-options-menu';
import { CoreUser } from '@features/user/services/user';
import { Component, HostBinding, Input } from '@angular/core';
import { CoreCourseListItem } from '@features/courses/services/courses';
/**
* This component is meant to display a course for a list of courses with progress.
@ -41,296 +28,15 @@ import { CoreUser } from '@features/user/services/user';
@Component({
selector: 'core-courses-course-progress',
templateUrl: 'core-courses-course-progress.html',
styleUrls: ['course-progress.scss'],
})
export class CoreCoursesCourseProgressComponent implements OnInit, OnDestroy, OnChanges {
export class CoreCoursesCourseProgressComponent {
// The course to render.
@Input() course!: CoreCourseAnyCourseDataWithExtraInfoAndOptions;
@Input() course!: CoreCourseListItem; // The course to render.
@Input() showAll = false; // If true, will show all actions, options, star and progress.
@Input() showDownload = true; // If true, will show download button. Only works if the options menu is not shown.
prefetchCourseData: CorePrefetchStatusInfo = {
icon: '',
statusTranslatable: 'core.loading',
status: '',
loading: true,
@HostBinding('class.deprecated') get isDeprecated(): boolean {
return true;
};
showSpinner = false;
downloadCourseEnabled = false;
courseOptionMenuEnabled = false;
isFavourite = false;
progress = -1;
completionUserTracked: boolean | undefined = false;
protected courseStatus = CoreConstants.NOT_DOWNLOADED;
protected isDestroyed = false;
protected courseStatusObserver?: CoreEventObserver;
protected siteUpdatedObserver?: CoreEventObserver;
/**
* @inheritdoc
*/
ngOnInit(): void {
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
if (this.downloadCourseEnabled) {
this.initPrefetchCourse();
}
// This field is only available from 3.6 onwards.
this.courseOptionMenuEnabled = this.showAll && 'isfavourite' in this.course &&
this.course.isfavourite !== undefined;
// Refresh the enabled flag if site is updated.
this.siteUpdatedObserver = CoreEvents.on(CoreEvents.SITE_UPDATED, () => {
const wasEnabled = this.downloadCourseEnabled;
this.downloadCourseEnabled = !CoreCourses.isDownloadCourseDisabledInSite();
if (!wasEnabled && this.downloadCourseEnabled) {
// Download course is enabled now, initialize it.
this.initPrefetchCourse();
}
}, CoreSites.getCurrentSiteId());
}
/**
* @inheritdoc
*/
ngOnChanges(): void {
this.isFavourite = 'isfavourite' in this.course && !!this.course.isfavourite;
this.progress = 'progress' in this.course ? this.course.progress || -1 : -1;
this.completionUserTracked = 'completionusertracked' in this.course && this.course.completionusertracked;
}
/**
* Initialize prefetch course.
*/
async initPrefetchCourse(): Promise<void> {
if (this.courseStatusObserver !== undefined) {
// Already initialized.
return;
}
// Listen for status change in course.
this.courseStatusObserver = CoreEvents.on(CoreEvents.COURSE_STATUS_CHANGED, (data: CoreEventCourseStatusChanged) => {
if (data.courseId == this.course.id || data.courseId == CoreCourseProvider.ALL_COURSES_CLEARED) {
this.updateCourseStatus(data.status);
}
}, CoreSites.getCurrentSiteId());
// Determine course prefetch icon.
const status = await CoreCourse.getCourseStatus(this.course.id);
this.updateCourseStatus(status);
if (this.prefetchCourseData.loading) {
// Course is being downloaded. Get the download promise.
const promise = CoreCourseHelper.getCourseDownloadPromise(this.course.id);
if (promise) {
// There is a download promise. If it fails, show an error.
promise.catch((error) => {
if (!this.isDestroyed) {
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
}
});
} else {
// No download, this probably means that the app was closed while downloading. Set previous status.
CoreCourse.setCoursePreviousStatus(this.course.id);
}
}
}
/**
* Open a course.
*/
openCourse(): void {
CoreCourseHelper.openCourse(this.course);
}
/**
* Prefetch the course.
*
* @param e Click event.
*/
async prefetchCourse(e?: Event): Promise<void> {
e?.preventDefault();
e?.stopPropagation();
try {
await CoreCourseHelper.confirmAndPrefetchCourse(this.prefetchCourseData, this.course);
} catch (error) {
if (!this.isDestroyed) {
CoreDomUtils.showErrorModalDefault(error, 'core.course.errordownloadingcourse', true);
}
}
}
/**
* Delete the course.
*/
async deleteCourse(): Promise<void> {
try {
await CoreDomUtils.showDeleteConfirm(
'addon.storagemanager.confirmdeletedatafrom',
{ name: this.course.displayname || this.course.fullname },
);
} catch (error) {
if (!CoreDomUtils.isCanceledError(error)) {
throw error;
}
return;
}
const modal = await CoreDomUtils.showModalLoading();
try {
await CoreCourseHelper.deleteCourseFiles(this.course.id);
} catch (error) {
CoreDomUtils.showErrorModalDefault(error, Translate.instant('core.errordeletefile'));
} finally {
modal.dismiss();
}
}
/**
* Update the course status icon and title.
*
* @param status Status to show.
*/
protected updateCourseStatus(status: string): void {
const statusData = CoreCourseHelper.getCoursePrefetchStatusInfo(status);
this.courseStatus = status;
this.prefetchCourseData.status = statusData.status;
this.prefetchCourseData.icon = statusData.icon;
this.prefetchCourseData.statusTranslatable = statusData.statusTranslatable;
this.prefetchCourseData.loading = statusData.loading;
}
/**
* Show the context menu.
*
* @param e Click Event.
*/
async showCourseOptionsMenu(e: Event): Promise<void> {
e.preventDefault();
e.stopPropagation();
const popoverData = await CoreDomUtils.openPopover<string>({
component: CoreCoursesCourseOptionsMenuComponent,
componentProps: {
course: this.course,
prefetch: this.prefetchCourseData,
},
event: e,
});
switch (popoverData) {
case 'download':
if (!this.prefetchCourseData.loading) {
this.prefetchCourse(e);
}
break;
case 'delete':
if (this.courseStatus == CoreConstants.DOWNLOADED || this.courseStatus == CoreConstants.OUTDATED) {
this.deleteCourse();
}
break;
case 'hide':
this.setCourseHidden(true);
break;
case 'show':
this.setCourseHidden(false);
break;
case 'favourite':
this.setCourseFavourite(true);
break;
case 'unfavourite':
this.setCourseFavourite(false);
break;
default:
break;
}
}
/**
* Hide/Unhide the course from the course list.
*
* @param hide True to hide and false to show.
*/
protected async setCourseHidden(hide: boolean): Promise<void> {
this.showSpinner = true;
// We should use null to unset the preference.
try {
await CoreUser.updateUserPreference(
'block_myoverview_hidden_course_' + this.course.id,
hide ? '1' : undefined,
);
(<CoreEnrolledCourseDataWithExtraInfoAndOptions> this.course).hidden = hide;
CoreEvents.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, {
courseId: this.course.id,
course: this.course,
action: CoreCoursesProvider.ACTION_STATE_CHANGED,
state: CoreCoursesProvider.STATE_HIDDEN,
value: hide,
}, CoreSites.getCurrentSiteId());
} catch (error) {
if (!this.isDestroyed) {
CoreDomUtils.showErrorModalDefault(error, 'Error changing course visibility.');
}
} finally {
this.showSpinner = false;
}
}
/**
* Favourite/Unfavourite the course from the course list.
*
* @param favourite True to favourite and false to unfavourite.
*/
protected async setCourseFavourite(favourite: boolean): Promise<void> {
this.showSpinner = true;
try {
await CoreCourses.setFavouriteCourse(this.course.id, favourite);
(<CoreEnrolledCourseDataWithExtraInfoAndOptions> this.course).isfavourite = favourite;
this.isFavourite = favourite;
CoreEvents.trigger(CoreCoursesProvider.EVENT_MY_COURSES_UPDATED, {
courseId: this.course.id,
course: this.course,
action: CoreCoursesProvider.ACTION_STATE_CHANGED,
state: CoreCoursesProvider.STATE_FAVOURITE,
value: favourite,
}, CoreSites.getCurrentSiteId());
} catch (error) {
if (!this.isDestroyed) {
CoreDomUtils.showErrorModalDefault(error, 'Error changing course favourite attribute.');
}
} finally {
this.showSpinner = false;
}
}
/**
* Component destroyed.
*/
ngOnDestroy(): void {
this.isDestroyed = true;
this.siteUpdatedObserver?.off();
this.courseStatusObserver?.off();
}
}

View File

@ -70,16 +70,6 @@ export class CoreCoursesProvider {
protected userCoursesIds?: Set<number>;
protected downloadOptionsEnabled = false;
/**
* Whether current site supports getting course options.
*
* @returns Whether current site supports getting course options.
* @deprecated since 4.0.
*/
canGetAdminAndNavOptions(): boolean {
return true;
}
/**
* Get categories. They can be filtered by id.
*
@ -602,26 +592,6 @@ export class CoreCoursesProvider {
});
}
/**
* Check if get courses by field WS is available in a certain site.
*
* @returns Whether get courses by field is available.
* @deprecated since 4.0.
*/
isGetCoursesByFieldAvailable(): boolean {
return true;
}
/**
* Check if get courses by field WS is available in a certain site, by site ID.
*
* @returns Promise resolved with boolean: whether get courses by field is available.
* @deprecated since 4.0.
*/
async isGetCoursesByFieldAvailableInSite(): Promise<boolean> {
return true;
}
/**
* Get the navigation and administration options for the given courses.
*
@ -1462,7 +1432,7 @@ export type CoreCourseGetCoursesData = CoreEnrolledCourseBasicData & {
/**
* Number of weeks/topics.
*
* @deprecated use courseformatoptions. This attribute is deprecated in moodle since 2.4 but still present.
* @deprecatedonmoodle since 2.4. Use courseformatoptions. This attribute is deprecated in moodle since 2.4 but still present.
*/
numsections?: number;
maxbytes?: number; // Largest size of file that can be uploaded into the course.
@ -1470,7 +1440,7 @@ export type CoreCourseGetCoursesData = CoreEnrolledCourseBasicData & {
/**
* How the hidden sections in the course are displayed to students.
*
* @deprecated use courseformatoptions. This attribute is deprecated in moodle since 2.4 but still present.
* @deprecatedonmoodle since 2.4. Use courseformatoptions. This attribute is deprecated in moodle since 2.4 but still present.
*/
hiddensections?: number;
groupmode?: number; // No group, separate, visible.

View File

@ -60,26 +60,6 @@ export class CoreFilterProvider {
});
}
/**
* Returns whether or not WS get available in context is available.
*
* @returns Promise resolved with true if ws is available, false otherwise.
* @deprecated since 4.0.
*/
async canGetAvailableInContext(): Promise<boolean> {
return true;
}
/**
* Returns whether or not WS get available in context is available in a certain site.
*
* @returns Promise resolved with true if ws is available, false otherwise.
* @deprecated since 4.0.
*/
canGetAvailableInContextInSite(): boolean {
return true;
}
/**
* Returns whether or not we can get the available filters: the WS is available and the feature isn't disabled.
*

View File

@ -60,49 +60,6 @@ export class CoreGradesHelperProvider {
this.logger = CoreLogger.getInstance('CoreGradesHelperProvider');
}
/**
* Formats a row from the grades table te be rendered in a page.
*
* @param tableRow JSON object representing row of grades table data.
* @returns Formatted row object.
* @deprecated since 4.0.
*/
protected async formatGradeRow(tableRow: CoreGradesTableRow): Promise<CoreGradesFormattedRow> {
const row: CoreGradesFormattedRow = {
rowclass: '',
};
for (const name in tableRow) {
const column: CoreGradesTableColumn = tableRow[name];
if (column.content === undefined || column.content === null) {
continue;
}
let content = String(column.content);
if (name == 'itemname') {
await this.setRowIconAndType(row, content);
row.link = this.getModuleLink(content);
row.rowclass += column.class.indexOf('hidden') >= 0 ? ' hidden' : '';
row.rowclass += column.class.indexOf('dimmed_text') >= 0 ? ' dimmed_text' : '';
content = content.replace(/<\/span>/gi, '\n');
content = CoreTextUtils.cleanTags(content);
} else {
content = CoreTextUtils.replaceNewLines(content, '<br>');
}
if (content == '&nbsp;') {
content = '';
}
row[name] = content.trim();
}
return row;
}
/**
* Formats a row from the grades table to be rendered in one table.
*
@ -353,34 +310,6 @@ export class CoreGradesHelperProvider {
return someCoursesAreMissing;
}
/**
* Get an specific grade item.
*
* @param courseId ID of the course to get the grades from.
* @param gradeId Grade ID.
* @param userId ID of the user to get the grades from. If not defined use site's current user.
* @param siteId Site ID. If not defined, current site.
* @param ignoreCache True if it should ignore cached data (it will always fail in offline or server down).
* @returns Promise to be resolved when the grades are retrieved.
* @deprecated since 4.0.
*/
async getGradeItem(
courseId: number,
gradeId: number,
userId?: number,
siteId?: string,
ignoreCache: boolean = false,
): Promise<CoreGradesFormattedRow | null> {
const grades = await CoreGrades.getCourseGradesTable(courseId, userId, siteId, ignoreCache);
if (!grades) {
throw new CoreError('Couldn\'t get grade item');
}
// eslint-disable-next-line deprecation/deprecation
return this.getGradesTableRow(grades, gradeId);
}
/**
* Returns the label of the selected grade.
*
@ -460,65 +389,6 @@ export class CoreGradesHelperProvider {
return link;
}
/**
* Get a row from the grades table.
*
* @param table JSON object representing a table with data.
* @param gradeId Grade Object identifier.
* @returns Formatted HTML table.
* @deprecated since 4.0.
*/
async getGradesTableRow(table: CoreGradesTable, gradeId: number): Promise<CoreGradesFormattedRow | null> {
if (table.tabledata) {
const selectedRow = table.tabledata.find(
(row) =>
row.itemname &&
row.itemname.id &&
row.itemname.id.substring(0, 3) == 'row' &&
parseInt(row.itemname.id.split('_')[1], 10) == gradeId,
);
if (selectedRow) {
// eslint-disable-next-line deprecation/deprecation
return await this.formatGradeRow(selectedRow);
}
}
return null;
}
/**
* Get the rows related to a module from the grades table.
*
* @param table JSON object representing a table with data.
* @param moduleId Grade Object identifier.
* @returns Formatted HTML table.
* @deprecated since 4.0.
*/
async getModuleGradesTableRows(table: CoreGradesTable, moduleId: number): Promise<CoreGradesFormattedRow[]> {
if (!table.tabledata) {
return [];
}
// Find href containing "/mod/xxx/xxx.php".
const regex = /href="([^"]*\/mod\/[^"|^/]*\/[^"|^.]*\.php[^"]*)/;
return Promise.all(table.tabledata.filter((row) => {
if (row.itemname && row.itemname.content) {
const matches = row.itemname.content.match(regex);
if (matches && matches.length) {
const hrefParams = CoreUrlUtils.extractUrlParams(matches[1]);
return hrefParams && parseInt(hrefParams.id) === moduleId;
}
}
return false;
// eslint-disable-next-line deprecation/deprecation
}).map((row) => this.formatGradeRow(row)));
}
/**
* Get module grades to display.
*

View File

@ -342,16 +342,6 @@ export class CoreGradesProvider {
return !(course && course.showgrades !== undefined && !course.showgrades);
}
/**
* Returns whether or not WS Grade Items is available.
*
* @returns True if ws is available, false otherwise.
* @deprecated since 4.0.
*/
async isGradeItemsAvailable(): Promise<boolean> {
return true;
}
/**
* Log Course grades view in Moodle.
*

View File

@ -44,16 +44,6 @@ export class CoreRatingProvider {
static readonly AGGREGATE_CHANGED_EVENT = 'core_rating_aggregate_changed';
static readonly RATING_SAVED_EVENT = 'core_rating_rating_saved';
/**
* Returns whether the web serivce to add ratings is available.
*
* @returns If WS is available.
* @deprecated since 4.0.
*/
isAddRatingWSAvailable(): boolean {
return true;
}
/**
* Add a rating to an item.
*

View File

@ -14,7 +14,7 @@
import { Component, OnDestroy, OnInit } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { ActivatedRoute, Params } from '@angular/router';
import { ActivatedRoute } from '@angular/router';
import { CoreSite, CoreSiteConfig } from '@classes/site';
import { CoreCourse, CoreCourseWSSection } from '@features/course/services/course';
@ -89,14 +89,7 @@ export class CoreSiteHomeIndexPage implements OnInit, OnDestroy {
const module = CoreNavigator.getRouteParam<CoreCourseModuleData>('module');
if (module) {
let modNavOptions = CoreNavigator.getRouteParam<CoreNavigationOptions>('modNavOptions');
if (!modNavOptions) {
// Fallback to old way of passing params. @deprecated since 4.0.
const modParams = CoreNavigator.getRouteParam<Params>('modParams');
if (modParams) {
modNavOptions = { params: modParams };
}
}
const modNavOptions = CoreNavigator.getRouteParam<CoreNavigationOptions>('modNavOptions');
CoreCourseHelper.openModule(module, this.siteHomeId, { modNavOptions });
}

View File

@ -40,8 +40,7 @@ export class CoreSitePluginsCourseFormatHandler extends CoreSitePluginsBaseHandl
*/
displayCourseIndex(): boolean {
// Use displaysectionselector while is not completely deprecated.
// eslint-disable-next-line deprecation/deprecation
return this.handlerSchema.displaycourseindex ?? this.handlerSchema.displaysectionselector ?? true;
return this.handlerSchema.displaycourseindex ?? true;
}
/**

View File

@ -12,11 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { Component, OnChanges, Input, ViewChild, Output, EventEmitter, HostBinding } from '@angular/core';
import { Component, OnChanges, Input, ViewChild, HostBinding } from '@angular/core';
import { IonRefresher } from '@ionic/angular';
import { CoreCourseFormatComponent } from '@features/course/components/course-format/course-format';
import { CoreCourseModuleCompletionData, CoreCourseSection } from '@features/course/services/course-helper';
import { CoreCourseSection } from '@features/course/services/course-helper';
import { CoreCourseFormatDelegate } from '@features/course/services/format-delegate';
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import { CoreSitePlugins, CoreSitePluginsContent } from '@features/siteplugins/services/siteplugins';
@ -37,12 +37,6 @@ export class CoreSitePluginsCourseFormatComponent implements OnChanges {
@Input() initialSectionId?: number; // The section to load first (by ID).
@Input() initialSectionNumber?: number; // The section to load first (by number).
@Input() moduleId?: number; // The module ID to scroll to. Must be inside the initial selected section.
/**
* Notify when any module completion changes.
*
* @deprecated since 4.0. Use CoreEvents instead.
*/
@Output() completionChanged = new EventEmitter<CoreCourseModuleCompletionData>();
// Special input, allows access to the parent instance properties and methods.
// Please notice that all the other inputs/outputs are also accessible through this instance, so they could be removed.
@ -87,8 +81,6 @@ export class CoreSitePluginsCourseFormatComponent implements OnChanges {
initialSectionId: this.initialSectionId,
initialSectionNumber: this.initialSectionNumber,
moduleId: this.moduleId,
// eslint-disable-next-line deprecation/deprecation
completionChanged: this.completionChanged,
coreCourseFormatComponent: this.coreCourseFormatComponent,
};
}

View File

@ -12,9 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
import { CoreConstants } from '@/core/constants';
import { Component, OnInit, OnDestroy, Input, ViewChild, HostBinding } from '@angular/core';
import { CoreIonLoadingElement } from '@classes/ion-loading';
import { CoreSiteWSPreSets } from '@classes/site';
import {
@ -22,12 +20,11 @@ import {
CoreCourseModuleSummaryComponent,
} from '@features/course/components/module-summary/module-summary';
import { CoreCourse } from '@features/course/services/course';
import { CoreCourseHelper, CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleData } from '@features/course/services/course-helper';
import {
CoreCourseModuleDelegate,
CoreCourseModuleMainComponent,
} from '@features/course/services/module-delegate';
import { CoreCourseModulePrefetchDelegate } from '@features/course/services/module-prefetch-delegate';
import {
CoreSitePlugins,
CoreSitePluginsContent,
@ -61,31 +58,6 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
preSets?: CoreSiteWSPreSets;
description?: string;
/**
* @deprecated since 4.0, use module.url instead.
*/
externalUrl?: string;
/**
* @deprecated since 4.0. It won't be populated anymore.
*/
refreshIcon = CoreConstants.ICON_REFRESH;
/**
* @deprecated since 4.0. It won't be populated anymore.
*/
prefetchStatus?: string;
/**
* @deprecated since 4.0. It won't be populated anymore.
*/
prefetchStatusIcon?: string;
/**
* @deprecated since 4.0. It won't be populated anymore.
*/
prefetchText?: string;
/**
* @deprecated since 4.0. It won't be populated anymore.
*/
size?: string;
collapsibleFooterAppearOnBottom = true;
displayOpenInBrowser = true;
@ -142,8 +114,6 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
// Get the data for the context menu.
this.description = this.module.description;
// eslint-disable-next-line deprecation/deprecation
this.externalUrl = this.module.url;
}
/**
@ -178,15 +148,6 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
return;
}
/**
* Expand the description.
*
* @deprecated since 4.0.
*/
expandDescription(): void {
this.openModuleSummary();
}
/**
* Opens a module summary page.
*/
@ -221,50 +182,7 @@ export class CoreSitePluginsModuleIndexComponent implements OnInit, OnDestroy, C
}
/**
* Prefetch the module.
*
* @deprecated since 4.0.
*/
async prefetch(): Promise<void> {
try {
// We need to call getDownloadSize, the package might have been updated.
const size = await CoreCourseModulePrefetchDelegate.getModuleDownloadSize(this.module, this.courseId, true);
await CoreDomUtils.confirmDownloadSize(size);
await CoreCourseModulePrefetchDelegate.prefetchModule(this.module, this.courseId, true);
} catch (error) {
if (!this.isDestroyed) {
CoreDomUtils.showErrorModalDefault(error, 'core.errordownloading', true);
}
}
}
/**
* Confirm and remove downloaded files.
*
* @deprecated since 4.0.
*/
async removeFiles(): Promise<void> {
let modal: CoreIonLoadingElement | undefined;
try {
await CoreDomUtils.showDeleteConfirm('addon.storagemanager.confirmdeletedatafrom', { name: this.module.name });
modal = await CoreDomUtils.showModalLoading();
await CoreCourseHelper.removeModuleStoredData(this.module, this.courseId);
} catch (error) {
if (error) {
CoreDomUtils.showErrorModal(error);
}
} finally {
modal?.dismiss();
}
}
/**
* Component destroyed.
* @inheritdoc
*/
ngOnDestroy(): void {
this.isDestroyed = true;

View File

@ -391,16 +391,6 @@ export class CoreSitePluginsProvider {
await site.invalidateWsCacheForKey(this.getContentCacheKey(component, callback, args || {}));
}
/**
* Check if the get content WS is available.
*
* @returns If get content WS is available.
* @deprecated since 4.0
*/
isGetContentAvailable(): boolean {
return true;
}
/**
* Check if a handler is enabled for a certain course.
*
@ -904,10 +894,6 @@ export type CoreSitePluginsCourseModuleHandlerData = CoreSitePluginsHandlerCommo
export type CoreSitePluginsCourseFormatHandlerData = CoreSitePluginsHandlerCommonData & {
canviewallsections?: boolean;
displayenabledownload?: boolean;
/**
* @deprecated since 4.0. Use displaycourseindex instead.
*/
displaysectionselector?: boolean;
displaycourseindex?: boolean;
};

View File

@ -109,26 +109,6 @@ export class CoreUserProvider {
return !!site?.wsAvailable('core_enrol_search_users');
}
/**
* Check if WS to update profile picture is available in site.
*
* @returns Promise resolved with boolean: whether it's available.
* @deprecated since 4.0.
*/
async canUpdatePicture(): Promise<boolean> {
return true;
}
/**
* Check if WS to search participants is available in site.
*
* @returns Whether it's available.
* @deprecated since 4.0.
*/
canUpdatePictureInSite(): boolean {
return true;
}
/**
* Change the given user profile picture.
*

View File

@ -411,63 +411,6 @@ export class CoreSitesProvider {
return new CoreLoginError(options);
}
/**
* Check if a site exists.
*
* @param siteUrl URL of the site to check.
* @returns A promise to be resolved if the site exists.
* @deprecated since 4.0. Now the app calls uses tool_mobile_get_public_config to check if site exists.
*/
async siteExists(siteUrl: string): Promise<void> {
let data: CoreSitesLoginTokenResponse;
// Use a valid path first.
siteUrl = CoreUrlUtils.removeUrlParams(siteUrl);
try {
const lang = await CoreLang.getCurrentLanguage(CoreLangFormat.LMS);
data = await Http.post(`${siteUrl}/login/token.php?lang=${lang}`, { appsitecheck: 1 })
.pipe(timeout(CoreWS.getRequestTimeout()))
.toPromise();
} catch (error) {
throw this.createCannotConnectLoginError(null, {
supportConfig: await CoreUserGuestSupportConfig.forSite(siteUrl),
errorcode: 'sitecheckfailed',
errorDetails: CoreDomUtils.getErrorMessage(error) ?? undefined,
});
}
if (data === null) {
// Cannot connect.
throw this.createCannotConnectLoginError(null, {
supportConfig: await CoreUserGuestSupportConfig.forSite(siteUrl),
errorcode: 'appsitecheckfailed',
errorDetails: 'A request to /login/token.php with appsitecheck=1 returned an empty response',
});
}
if (data.errorcode && (data.errorcode == 'enablewsdescription' || data.errorcode == 'requirecorrectaccess')) {
throw this.createCannotConnectLoginError(siteUrl, {
supportConfig: await CoreUserGuestSupportConfig.forSite(siteUrl),
critical: data.errorcode == 'enablewsdescription',
errorcode: data.errorcode,
errorDetails: data.error,
});
}
if (data.error && data.error == 'Web services must be enabled in Advanced features.') {
throw this.createCannotConnectLoginError(siteUrl, {
supportConfig: await CoreUserGuestSupportConfig.forSite(siteUrl),
critical: true,
errorcode: 'enablewsdescription',
errorDetails: data.error,
});
}
// Other errors are not being checked because invalid login will be always raised and we cannot differ them.
}
/**
* Gets a user token from the server.
*
@ -702,16 +645,6 @@ export class CoreSitesProvider {
return <string> Md5.hashAsciiStr(siteUrl + username);
}
/**
* Function for determine which service we should use (default or extended plugin).
*
* @returns The service shortname.
* @deprecated since 4.0.
*/
determineService(): string {
return CoreConstants.CONFIG.wsservice;
}
/**
* Visit a site link.
*
@ -2184,13 +2117,6 @@ export type CoreSiteCheckResponse = {
*/
service: string;
/**
* Code of the warning message to show to the user.
*
* @deprecated since 4.0.
*/
warning?: string;
/**
* Site public config (if available).
*/

View File

@ -49,8 +49,6 @@ import { CoreSites } from '@services/sites';
import { NavigationStart } from '@angular/router';
import { filter } from 'rxjs/operators';
import { Subscription } from 'rxjs';
import { CoreDirectivesRegistry } from '@singletons/directives-registry';
import { CoreDom } from '@singletons/dom';
import { CoreNetwork } from '@services/network';
import { CoreSiteError } from '@classes/errors/siteerror';
import { CoreUserSupport } from '@features/user/services/support';
@ -99,20 +97,6 @@ export class CoreDomUtilsProvider {
this.debugDisplay = debugDisplay != 0;
}
/**
* Equivalent to element.closest(). If the browser doesn't support element.closest, it will
* traverse the parents to achieve the same functionality.
* Returns the closest ancestor of the current element (or the current element itself) which matches the selector.
*
* @param element DOM Element.
* @param selector Selector to search.
* @returns Closest ancestor.
* @deprecated since 4.0. Not needed anymore since it's supported on both Android and iOS. Use closest instead.
*/
closest(element: Element | undefined | null, selector: string): Element | null {
return element?.closest(selector) ?? null;
}
/**
* If the download size is higher than a certain threshold shows a confirm dialog.
*
@ -396,91 +380,6 @@ export class CoreDomUtilsProvider {
return this.convertToElement(html).children[0].getAttribute(attribute);
}
/**
* Returns height of an element.
*
* @param element DOM element to measure.
* @param usePadding Whether to use padding to calculate the measure.
* @param useMargin Whether to use margin to calculate the measure.
* @param useBorder Whether to use borders to calculate the measure.
* @param innerMeasure If inner measure is needed: padding, margin or borders will be substracted.
* @returns Height in pixels.
* @deprecated since 4.0 Use getBoundingClientRect.height instead.
*/
getElementHeight(
element: HTMLElement,
usePadding?: boolean,
useMargin?: boolean,
useBorder?: boolean,
innerMeasure?: boolean,
): number {
// eslint-disable-next-line deprecation/deprecation
return this.getElementMeasure(element, false, usePadding, useMargin, useBorder, innerMeasure);
}
/**
* Returns height or width of an element.
*
* @param element DOM element to measure.
* @param getWidth Whether to get width or height.
* @param usePadding Whether to use padding to calculate the measure.
* @param useMargin Whether to use margin to calculate the measure.
* @param useBorder Whether to use borders to calculate the measure.
* @param innerMeasure If inner measure is needed: padding, margin or borders will be substracted.
* @returns Measure in pixels.
* @deprecated since 4.0. Use getBoundingClientRect.height or width instead.
*/
getElementMeasure(
element: HTMLElement,
getWidth?: boolean,
usePadding?: boolean,
useMargin?: boolean,
useBorder?: boolean,
innerMeasure?: boolean,
): number {
const offsetMeasure = getWidth ? 'offsetWidth' : 'offsetHeight';
const measureName = getWidth ? 'width' : 'height';
const clientMeasure = getWidth ? 'clientWidth' : 'clientHeight';
const priorSide = getWidth ? 'Left' : 'Top';
const afterSide = getWidth ? 'Right' : 'Bottom';
let measure = element[offsetMeasure] || element[measureName] || element[clientMeasure] || 0;
// Measure not correctly taken.
if (measure <= 0) {
const style = getComputedStyle(element);
if (style?.display == '') {
element.style.display = 'inline-block';
measure = element[offsetMeasure] || element[measureName] || element[clientMeasure] || 0;
element.style.display = '';
}
}
if (usePadding || useMargin || useBorder) {
const computedStyle = getComputedStyle(element);
let surround = 0;
if (usePadding) {
surround += this.getComputedStyleMeasure(computedStyle, 'padding' + priorSide) +
this.getComputedStyleMeasure(computedStyle, 'padding' + afterSide);
}
if (useMargin) {
surround += this.getComputedStyleMeasure(computedStyle, 'margin' + priorSide) +
this.getComputedStyleMeasure(computedStyle, 'margin' + afterSide);
}
if (useBorder) {
surround += this.getComputedStyleMeasure(computedStyle, 'border' + priorSide + 'Width') +
this.getComputedStyleMeasure(computedStyle, 'border' + afterSide + 'Width');
}
if (innerMeasure) {
measure = measure > surround ? measure - surround : 0;
} else {
measure += surround;
}
}
return measure;
}
/**
* Returns the computed style measure or 0 if not found or NaN.
*
@ -492,62 +391,6 @@ export class CoreDomUtilsProvider {
return parseInt(style[measure], 10) || 0;
}
/**
* Returns width of an element.
*
* @param element DOM element to measure.
* @param usePadding Whether to use padding to calculate the measure.
* @param useMargin Whether to use margin to calculate the measure.
* @param useBorder Whether to use borders to calculate the measure.
* @param innerMeasure If inner measure is needed: padding, margin or borders will be substracted.
* @returns Width in pixels.
* @deprecated since 4.0. Use getBoundingClientRect.width instead.
*/
getElementWidth(
element: HTMLElement,
usePadding?: boolean,
useMargin?: boolean,
useBorder?: boolean,
innerMeasure?: boolean,
): number {
// eslint-disable-next-line deprecation/deprecation
return this.getElementMeasure(element, true, usePadding, useMargin, useBorder, innerMeasure);
}
/**
* Retrieve the position of a element relative to another element.
*
* @param element Element to search in.
* @param selector Selector to find the element to gets the position.
* @param positionParentClass Parent Class where to stop calculating the position. Default inner-scroll.
* @returns positionLeft, positionTop of the element relative to.
* @deprecated since 4.0. Use CoreDom.getRelativeElementPosition instead.
*/
getElementXY(element: HTMLElement, selector?: string, positionParentClass = 'inner-scroll'): [number, number] | null {
if (selector) {
const foundElement = element.querySelector<HTMLElement>(selector);
if (!foundElement) {
// Element not found.
return null;
}
element = foundElement;
}
const parent = element.closest<HTMLElement>(`.${positionParentClass}`);
if (!parent) {
return null;
}
const position = CoreDom.getRelativeElementPosition(element, parent);
// Calculate the top and left positions.
return [
Math.ceil(position.x),
Math.ceil(position.y),
];
}
/**
* Given a message, it deduce if it's a network error.
*
@ -639,19 +482,6 @@ export class CoreDomUtilsProvider {
return message;
}
/**
* Retrieve component/directive instance.
* Please use this function only if you cannot retrieve the instance using parent/child methods: ViewChild (or similar)
* or Angular's injection.
*
* @param element The root element of the component/directive.
* @returns The instance, undefined if not found.
* @deprecated since 4.0. Use CoreDirectivesRegistry instead.
*/
getInstanceByElement<T = unknown>(element: Element): T | undefined {
return CoreDirectivesRegistry.resolve<T>(element) ?? undefined;
}
/**
* Check whether an error is an error caused because the user canceled a showConfirm.
*
@ -672,36 +502,6 @@ export class CoreDomUtilsProvider {
return error instanceof CoreSilentError;
}
/**
* Wait an element to exists using the findFunction.
*
* @param findFunction The function used to find the element.
* @param retries Number of retries before giving up.
* @param retryAfter Milliseconds to wait before retrying if the element wasn't found.
* @returns Resolved if found, rejected if too many tries.
* @deprecated since 4.0. Use CoreDom.waitToBeInsideElement instead.
*/
async waitElementToExist(
findFunction: () => HTMLElement | null,
retries: number = 100,
retryAfter: number = 100,
): Promise<HTMLElement> {
const element = findFunction();
if (!element && retries === 0) {
throw Error('Element not found');
}
if (!element) {
await CoreUtils.wait(retryAfter);
// eslint-disable-next-line deprecation/deprecation
return this.waitElementToExist(findFunction, retries - 1);
}
return element;
}
/**
* Handle bootstrap tooltips in a certain element.
*
@ -853,28 +653,6 @@ export class CoreDomUtilsProvider {
return element.innerHTML;
}
/**
* Remove a component/directive instance using the DOM Element.
*
* @param element The root element of the component/directive.
* @deprecated since 4.0. It's no longer necessary to remove instances.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
removeInstanceByElement(element: Element): void {
//
}
/**
* Remove a component/directive instance using the ID.
*
* @param id The ID to remove.
* @deprecated since 4.0. It's no longer necessary to remove instances.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
removeInstanceById(id: string): void {
//
}
/**
* Search for certain classes in an element contents and replace them with the specified new values.
*
@ -998,67 +776,6 @@ export class CoreDomUtilsProvider {
}
}
/**
* Scroll to a certain element.
*
* @param content Not used anymore.
* @param element The element to scroll to.
* @param scrollParentClass Not used anymore.
* @param duration Duration of the scroll animation in milliseconds.
* @returns True if the element is found, false otherwise.
* @deprecated since 4.0. Use CoreDom.scrollToElement instead.
*/
scrollToElement(content: IonContent, element: HTMLElement, scrollParentClass?: string, duration?: number): boolean {
CoreDom.scrollToElement(element, undefined, { duration });
return true;
}
/**
* Scroll to a certain element using a selector to find it.
*
* @param container The element that contains the element that must be scrolled.
* @param content Not used anymore.
* @param selector Selector to find the element to scroll to.
* @param scrollParentClass Not used anymore.
* @param duration Duration of the scroll animation in milliseconds.
* @returns True if the element is found, false otherwise.
* @deprecated since 4.0. Use CoreDom.scrollToElement instead.
*/
scrollToElementBySelector(
container: HTMLElement | null,
content: unknown | null,
selector: string,
scrollParentClass?: string,
duration?: number,
): boolean {
if (!container || !content) {
return false;
}
CoreDom.scrollToElement(container, selector, { duration });
return true;
}
/**
* Search for an input with error (core-input-error directive) and scrolls to it if found.
*
* @param container The element that contains the element that must be scrolled.
* @returns True if the element is found, false otherwise.
* @deprecated since 4.0. Use CoreDom.scrollToInputError instead.
*/
scrollToInputError(container: HTMLElement | null): boolean {
if (!container) {
return false;
}
CoreDom.scrollToInputError(container);
return true;
}
/**
* Set whether debug messages should be displayed.
*
@ -1669,17 +1386,6 @@ export class CoreDomUtilsProvider {
return loader;
}
/**
* Stores a component/directive instance.
*
* @param element The root element of the component/directive.
* @param instance The instance to store.
* @deprecated since 4.0. Use CoreDirectivesRegistry instead.
*/
storeInstanceByElement(element: Element, instance: unknown): void {
CoreDirectivesRegistry.register(element, instance);
}
/**
* Check if an element supports input via keyboard.
*

View File

@ -298,18 +298,6 @@ export class CoreTextUtilsProvider {
return text;
}
/**
* Concatenate two paths, adding a slash between them if needed.
*
* @param leftPath Left path.
* @param rightPath Right path.
* @returns Concatenated path.
* @deprecated since 4.0. Use CorePath instead.
*/
concatenatePaths(leftPath: string, rightPath: string): string {
return CorePath.concatenatePaths(leftPath, rightPath);
}
/**
* Convert some HTML as text into an HTMLElement. This HTML is put inside a div or a body.
* This function is the same as in DomUtils, but we cannot use that one because of circular dependencies.

View File

@ -16,7 +16,6 @@ import { Injectable } from '@angular/core';
import moment, { LongDateFormatKey } from 'moment-timezone';
import { makeSingleton, Translate } from '@singletons';
import { CoreTime } from '@singletons/time';
/*
* "Utils" service with helper functions for date and time.
@ -155,52 +154,6 @@ export class CoreTimeUtilsProvider {
return fixed;
}
/**
* Returns years, months, days, hours, minutes and seconds in a human readable format.
*
* @param seconds A number of seconds
* @param precision Number of elements to have in precision.
* @returns Seconds in a human readable format.
* @deprecated since 4.0. Use CoreTime.formatTime instead.
*/
formatTime(seconds: number, precision = 2): string {
return CoreTime.formatTime(seconds, precision);
}
/**
* Converts a number of seconds into a short human readable format: minutes and seconds, in fromat: 3' 27''.
*
* @param duration Seconds
* @returns Short human readable text.
* @deprecated since 4.0. Use CoreTime.formatTimeShort instead.
*/
formatTimeShort(duration: number): string {
return CoreTime.formatTimeShort(duration);
}
/**
* Returns hours, minutes and seconds in a human readable format.
*
* @param duration Duration in seconds
* @param precision Number of elements to have in precision. 0 or undefined to full precission.
* @returns Duration in a human readable format.
* @deprecated since 4.0. Use CoreTime.formatTime instead.
*/
formatDuration(duration: number, precision?: number): string {
return CoreTime.formatTime(duration, precision);
}
/**
* Returns duration in a short human readable format: minutes and seconds, in fromat: 3' 27''.
*
* @param duration Duration in seconds
* @returns Duration in a short human readable format.
* @deprecated since 4.0. Use CoreTime.formatTimeShort instead.
*/
formatDurationShort(duration: number): string {
return CoreTime.formatTimeShort(duration);
}
/**
* Return the current timestamp in a "readable" format: YYYYMMDDHHmmSS.
*

View File

@ -39,8 +39,6 @@ export interface CoreEventsData {
[CoreEvents.SITE_ADDED]: CoreEventSiteAddedData;
[CoreEvents.SITE_DELETED]: CoreSite;
[CoreEvents.SESSION_EXPIRED]: CoreEventSessionExpiredData;
// eslint-disable-next-line deprecation/deprecation
[CoreEvents.CORE_LOADING_CHANGED]: CoreEventLoadingChangedData;
[CoreEvents.COURSE_STATUS_CHANGED]: CoreEventCourseStatusChanged;
[CoreEvents.PACKAGE_STATUS_CHANGED]: CoreEventPackageStatusChanged;
[CoreEvents.USER_DELETED]: CoreEventUserDeletedData;
@ -104,10 +102,6 @@ export class CoreEvents {
static readonly APP_LAUNCHED_URL = 'app_launched_url'; // App opened with a certain URL (custom URL scheme).
static readonly FILE_SHARED = 'file_shared';
static readonly KEYBOARD_CHANGE = 'keyboard_change';
/**
* @deprecated since 4.0. Use CoreDirectivesRegistry promises instead.
*/
static readonly CORE_LOADING_CHANGED = 'core_loading_changed';
static readonly ORIENTATION_CHANGE = 'orientation_change';
static readonly SEND_ON_ENTER_CHANGED = 'send_on_enter_changed';
static readonly SELECT_COURSE_TAB = 'select_course_tab';