Merge pull request #3422 from crazyserver/MOBILE-4081

Mobile 4081
main
Noel De Martin 2022-10-27 15:35:07 +02:00 committed by GitHub
commit 8b1113ab37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1862 additions and 1752 deletions

View File

@ -2,7 +2,7 @@ name: Mirror
on:
push:
branches: [ master, integration ]
branches: [ master, main, unscheduled, integration, beta ]
jobs:
mirror:

View File

@ -2,7 +2,7 @@ name: Prepare
on:
push:
branches: [ master, integration, freemium-master ]
branches: [ master, main, unscheduled, integration, beta, freemium-master, freemium-main ]
jobs:
prepare:

View File

@ -1,6 +1,8 @@
os: linux
dist: trusty
node_js: 14
jdk:
- oraclejdk11
git:
depth: 3
@ -51,7 +53,10 @@ jobs:
- npm --version
- nvm --version
- npm ci
- export JAVA_HOME_COPY=$JAVA_HOME
- export JAVA_HOME=/usr/lib/jvm/java-8-oracle
- yes | sdkmanager "build-tools;30.0.3"
- export JAVA_HOME=$JAVA_HOME_COPY
addons:
apt:
packages:

View File

@ -40,7 +40,7 @@
<preference name="SplashMaintainAspectRatio" value="true" />
<preference name="SplashShowOnlyFirstTime" value="false" />
<preference name="android-minSdkVersion" value="22" />
<preference name="android-targetSdkVersion" value="30" />
<preference name="android-targetSdkVersion" value="31" />
<preference name="AndroidPersistentFileLocation" value="Compatibility" />
<preference name="AndroidInsecureFileModeEnabled" value="true" />
<preference name="CustomURLSchemePluginClearsAndroidIntent" value="true" />
@ -64,7 +64,7 @@
<resource-file src="resources/android/icon/drawable-xhdpi-smallicon.png" target="app/src/main/res/mipmap-xhdpi/smallicon.png" />
<resource-file src="resources/android/xml/network_security_config.xml" target="app/src/main/res/xml/network_security_config.xml" />
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application/activity[@android:name='MainActivity']">
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|screenLayout|smallestScreenSize" />
<activity android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|screenLayout|smallestScreenSize" android:exported="true" />
</edit-config>
<edit-config file="AndroidManifest.xml" mode="merge" target="/manifest/application">
<application android:largeHeap="true" android:networkSecurityConfig="@xml/network_security_config" />
@ -196,21 +196,6 @@
<param name="android-package" value="com.adobe.phonegap.push.PushPlugin" />
</feature>
</config-file>
<config-file parent="/manifest/application" target="AndroidManifest.xml">
<activity android:exported="true" android:name="com.adobe.phonegap.push.PushHandlerActivity" android:permission="${applicationId}.permission.PushHandlerActivity" />
<receiver android:name="com.adobe.phonegap.push.BackgroundActionButtonHandler" />
<receiver android:name="com.adobe.phonegap.push.PushDismissedHandler" />
<service android:name="com.adobe.phonegap.push.FCMService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<service android:name="com.adobe.phonegap.push.PushInstanceIDListenerService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
</config-file>
<config-file parent="/*" target="res/xml/config.xml">
<feature name="Media">
<param name="android-package" value="org.apache.cordova.media.AudioHandler" />

View File

@ -241,13 +241,13 @@
"publisher": "Ionic Team",
"licenseFile": "LICENSE"
},
"@moodlehq/cordova-plugin-local-notification@0.9.0-moodle.3": {
"@moodlehq/cordova-plugin-local-notification@0.9.0-moodle.7": {
"licenses": "Apache*",
"repository": "https://github.com/moodlemobile/cordova-plugin-local-notification",
"publisher": "Sebastián Katzer",
"licenseFile": "LICENSE"
},
"@moodlehq/cordova-plugin-qrscanner@3.0.1-moodle.2": {
"@moodlehq/cordova-plugin-qrscanner@3.0.1-moodle.4": {
"licenses": "MIT",
"repository": "https://github.com/moodlemobile/cordova-plugin-qrscanner",
"publisher": "Jason Dreyzehner",
@ -258,7 +258,7 @@
"repository": "https://github.com/moodlemobile/cordova-plugin-zip",
"licenseFile": "LICENSE"
},
"@moodlehq/phonegap-plugin-push@2.0.0-moodle.4": {
"@moodlehq/phonegap-plugin-push@4.0.0-moodle.2": {
"licenses": "MIT",
"repository": "https://github.com/moodlemobile/phonegap-plugin-push",
"publisher": "Erisu",
@ -2669,7 +2669,7 @@
"url": "https://github.com/ichernev",
"licenseFile": "LICENSE"
},
"moodlemobile@4.0.0": {
"moodlemobile@4.0.2": {
"licenses": "Apache-2.0",
"repository": "https://github.com/moodlehq/moodleapp",
"publisher": "Moodle Pty Ltd.",

3455
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -77,10 +77,10 @@
"@moodlehq/cordova-plugin-file-transfer": "1.7.1-moodle.5",
"@moodlehq/cordova-plugin-inappbrowser": "5.0.0-moodle.3",
"@moodlehq/cordova-plugin-ionic-webview": "5.0.0-moodle.1",
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.3",
"@moodlehq/cordova-plugin-local-notification": "0.9.0-moodle.7",
"@moodlehq/cordova-plugin-qrscanner": "3.0.1-moodle.4",
"@moodlehq/cordova-plugin-zip": "3.1.0-moodle.1",
"@moodlehq/phonegap-plugin-push": "2.0.0-moodle.4",
"@moodlehq/phonegap-plugin-push": "4.0.0-moodle.2",
"@ngx-translate/core": "^13.0.0",
"@ngx-translate/http-loader": "^6.0.0",
"@types/chart.js": "^2.9.31",
@ -232,9 +232,8 @@
"@moodlehq/cordova-plugin-zip": {},
"cordova-sqlite-storage": {},
"@moodlehq/phonegap-plugin-push": {
"ANDROID_SUPPORT_V13_VERSION": "28.0.0",
"FCM_VERSION": "18.+",
"IOS_FIREBASE_MESSAGING_VERSION": "~> 6.32.2"
"ANDROIDX_CORE_VERSION": "1.6.+",
"FCM_VERSION": "23.+"
},
"com-darryncampbell-cordova-plugin-intent": {},
"nl.kingsquare.cordova.background-audio": {},

View File

@ -14,7 +14,7 @@ print_title 'Generating language from code...'
npx gulp lang
print_title 'Getting local mobile langs'
git clone --branch integration --depth 1 https://github.com/moodlehq/moodle-local_moodlemobileapp.git ../../moodle-local_moodlemobileapp
git clone --branch master --depth 1 https://github.com/moodlehq/moodle-local_moodlemobileapp.git ../../moodle-local_moodlemobileapp
if [ -z $forceLang ]; then
get_languages

View File

@ -15,7 +15,10 @@
import { CoreConstants, ModPurpose } from '@/core/constants';
import { Injectable, Type } from '@angular/core';
import { CoreModuleHandlerBase } from '@features/course/classes/module-base-handler';
import { CoreCourseModuleHandler } from '@features/course/services/module-delegate';
import { CoreCourseModuleData } from '@features/course/services/course-helper';
import { CoreCourseModuleHandler, CoreCourseModuleHandlerData } from '@features/course/services/module-delegate';
import { CoreNavigator } from '@services/navigator';
import { CoreDomUtils } from '@services/utils/dom';
import { makeSingleton } from '@singletons';
import { AddonModFolderIndexComponent } from '../../components/index';
@ -44,6 +47,41 @@ export class AddonModFolderModuleHandlerService extends CoreModuleHandlerBase im
[CoreConstants.FEATURE_MOD_PURPOSE]: ModPurpose.MOD_PURPOSE_CONTENT,
};
/**
* @inheritdoc
*/
async getData(
module: CoreCourseModuleData,
courseId: number,
sectionId?: number,
forCoursePage?: boolean,
): Promise<CoreCourseModuleHandlerData> {
const data = await super.getData(module, courseId, sectionId, forCoursePage);
if (module.description) {
// Module description can contain the folder contents if it's inline, remove it.
const descriptionElement = CoreDomUtils.convertToElement(module.description);
Array.from(descriptionElement.querySelectorAll('.foldertree, .folderbuttons, .tertiary-navigation'))
.forEach(element => element.remove());
module.description = descriptionElement.innerHTML;
}
// @todo: Temporary fix to open inline folders. We should use a more generic solution.
data.action = async (event, module, courseId, options): Promise<void> => {
options = options || {};
options.params = options.params || {};
Object.assign(options.params, { module });
const routeParams = '/' + courseId + '/' + module.id;
await CoreNavigator.navigateToSitePath(this.pageName + routeParams, options);
};
return data;
}
/**
* @inheritdoc
*/

View File

@ -845,8 +845,6 @@ export class AddonModForumProvider {
forumId: number,
options: AddonModForumGetDiscussionsInPagesOptions = {},
): Promise<{ discussions: AddonModForumDiscussion[]; error: boolean }> {
options.page = options.page || 0;
const result = {
discussions: [] as AddonModForumDiscussion[],
error: false,
@ -859,7 +857,10 @@ export class AddonModForumProvider {
const getPage = (page: number): Promise<{ discussions: AddonModForumDiscussion[]; error: boolean }> =>
// Get page discussions.
this.getDiscussions(forumId, options).then((response) => {
this.getDiscussions(forumId, {
...options,
page,
}).then((response) => {
result.discussions = result.discussions.concat(response.discussions);
numPages--;
@ -876,7 +877,7 @@ export class AddonModForumProvider {
})
;
return getPage(options.page);
return getPage(options.page ?? 0);
}
/**
@ -916,7 +917,7 @@ export class AddonModForumProvider {
.getDiscussionsInPages(forum.id, {
cmId: forum.cmid,
sortOrder: sortOrder.value,
readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE,
readingStrategy: CoreSitesReadingStrategy.ONLY_CACHE,
})
.then((response) => {
// Now invalidate the WS calls.

View File

@ -128,7 +128,7 @@
<core-download-refresh *ngIf="downloadEnabled && module.handlerData?.showDownloadButton &&
module.downloadStatus != statusDownloaded" [status]="module.downloadStatus" [enabled]="true"
[canTrustDownload]="true" [loading]="module.spinner || module.handlerData.spinner"
(action)="prefetchModule(module, section)">
(action)="prefetchModule(module, $event)">
</core-download-refresh>
<ion-button fill="clear" (click)="deleteForModule(module, section)"
*ngIf="!module.calculatingSize && module.totalSize > 0" color="danger">

View File

@ -104,7 +104,7 @@ export class CoreSite {
'3.9': 2020061500,
'3.10': 2020110900,
'3.11': 2021051700,
'4.0': 2021100300, // @todo [4.0] replace with right value when released. Using a tmp value to be able to test new things.
'4.0': 2022041900,
};
// Possible cache update frequencies.

View File

@ -195,7 +195,9 @@ export class CoreBlockDelegateService extends CoreDelegate<CoreBlockHandler> {
* @return Whether is enabled or disabled in site.
*/
protected isFeatureDisabled(handler: CoreBlockHandler, site: CoreSite): boolean {
return this.areBlocksDisabledInSite(site) || super.isFeatureDisabled(handler, site);
// Allow displaying my overview even if all blocks are disabled, to avoid having an empty My Courses.
return (this.areBlocksDisabledInSite(site) && handler.blockName !== 'myoverview') ||
super.isFeatureDisabled(handler, site);
}
/**

View File

@ -24,8 +24,8 @@
</core-format-text>
</h1>
</ion-label>
<ion-button fill="clear" *ngIf="displayOptions.displayOpenInBrowser" [href]="externalUrl" core-link [showBrowserWarning]="false"
[attr.aria-label]="'core.openinbrowser' | translate" slot="end">
<ion-button fill="clear" *ngIf="displayOptions.displayOpenInBrowser && externalUrl" [href]="externalUrl" core-link
[showBrowserWarning]="false" [attr.aria-label]="'core.openinbrowser' | translate" slot="end">
<ion-icon name="fas-external-link-alt" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
</ion-item>

View File

@ -27,8 +27,7 @@
</div>
</ng-container>
<ion-item class="ion-text-wrap" button detail="false" (click)="openCourse()" [attr.aria-label]="course.displayname || course.fullname"
[class.item-disabled]="course.visible == 0">
<ion-item class="ion-text-wrap" button detail="false" (click)="openCourse()" [attr.aria-label]="course.displayname || course.fullname">
<ng-container *ngIf="layout == 'list' || layout == 'listwithenrol'">
<ion-icon *ngIf="!course.courseImage" name="fas-graduation-cap" slot="start" class="course-icon core-course-thumb"
@ -74,6 +73,13 @@
</core-format-text>
</ion-label>
</ion-chip>
<ion-chip color="info" *ngIf="course.visible == 0"
class="core-course-additional-info ion-text-wrap core-course-hidden-message">
<ion-label>
{{ 'core.course.hiddenfromstudents' | translate }}
</ion-label>
</ion-chip>
</div>
<div *ngIf="layout != 'summarycard' && isEnrolled && progress >= 0 && completionUserTracked !== false"

View File

@ -97,6 +97,10 @@ ion-chip {
margin-left: 0;
margin-right: 0;
max-width: 100%;
&.core-course-category {
@include margin-horizontal(0px, 8px);
}
}

View File

@ -4,7 +4,6 @@
<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.item-disabled]="course.visible == 0"
[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)"

View File

@ -113,6 +113,7 @@
}
}
// Common styles.
:host-context(.core-horizontal-scroll) {
@include horizontal_scroll_item(80%, 250px, 300px);

View File

@ -18,6 +18,7 @@ import { AsyncComponent } from '@classes/async-component';
import { PageLoadsManager } from '@classes/page-loads-manager';
import { CorePromisedValue } from '@classes/promised-value';
import { CoreBlockComponent } from '@features/block/components/block/block';
import { CoreBlockDelegate } from '@features/block/services/block-delegate';
import { CoreCourseBlock } from '@features/course/services/course';
import { CoreCoursesDashboard, CoreCoursesDashboardProvider } from '@features/courses/services/dashboard';
import { CoreMainMenuDeepLinkManager } from '@features/mainmenu/classes/deep-link-manager';
@ -98,22 +99,30 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy, AsyncCompone
const available = await CoreCoursesDashboard.isAvailable();
const disabled = await CoreCourses.isMyCoursesDisabled();
const supportsMyParam = !!CoreSites.getCurrentSite()?.isVersionGreaterEqualThan('4.0');
if (available && !disabled) {
try {
const blocks = await loadWatcher.watchRequest(
CoreCoursesDashboard.getDashboardBlocksObservable({
myPage: this.myPageCourses,
myPage: supportsMyParam ? this.myPageCourses : undefined,
readingStrategy: loadWatcher.getReadingStrategy(),
}),
);
// My overview block should always be in main blocks, but check side blocks too just in case.
this.loadedBlock = blocks.mainBlocks.concat(blocks.sideBlocks).find((block) => block.name == 'myoverview');
this.hasSideBlocks = blocks.sideBlocks.length > 0;
this.hasSideBlocks = supportsMyParam && CoreBlockDelegate.hasSupportedBlock(blocks.sideBlocks);
await CoreUtils.nextTicks(2);
this.myOverviewBlock = this.block?.dynamicComponent?.instance as AddonBlockMyOverviewComponent;
if (!this.loadedBlock && !supportsMyParam) {
// In old sites, display the block even if not found in Dashboard.
// This is because the "My courses" page doesn't exist in the site so it can't be configured.
this.loadFallbackBlock();
}
} catch (error) {
CoreDomUtils.showErrorModal(error);
@ -121,10 +130,9 @@ export class CoreCoursesMyCoursesPage implements OnInit, OnDestroy, AsyncCompone
this.loadFallbackBlock();
}
} else if (!available) {
// WS not available, or my courses page not available. show fallback block.
// WS not available, show fallback block.
this.loadFallbackBlock();
} else {
// Disabled.
this.loadedBlock = undefined;
}

View File

@ -667,9 +667,14 @@ export class CorePushNotificationsProvider {
const pushObject = Push.init(options);
pushObject.on('notification').subscribe((notification: NotificationEventResponse) => {
pushObject.on('notification').subscribe((notification: NotificationEventResponse | {registrationType: string}) => {
// Execute the callback in the Angular zone, so change detection doesn't stop working.
NgZone.run(() => {
if ('registrationType' in notification) {
// Not a valid notification, ignore.
return;
}
this.logger.log('Received a notification', notification);
this.onMessageReceived(notification);
});

View File

@ -84,7 +84,8 @@ import { CoreContentLinksModuleIndexHandler } from '@features/contentlinks/class
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
import { CoreContentLinksModuleListHandler } from '@features/contentlinks/classes/module-list-handler';
import { CoreObject } from '@singletons/object';
import { CoreUrl } from '@singletons/url';
import { CoreUrlUtils } from '@services/utils/url';
import { CoreText } from '@singletons/text';
const HANDLER_DISABLED = 'core_site_plugins_helper_handler_disabled';
@ -164,8 +165,11 @@ export class CoreSitePluginsHelperProvider {
): Promise<string> {
const site = await CoreSites.getSite(siteId);
// Make sure it's an absolute URL.
let url = handlerSchema.styles?.url ? CoreUrl.toAbsoluteURL(site.getURL(), handlerSchema.styles.url) : undefined;
// Make sure it's an absolute URL. Do not use toAbsoluteURL because it can change the behaviour and break plugin styles.
let url = handlerSchema.styles?.url;
if (url && !CoreUrlUtils.isAbsoluteURL(url)) {
url = CoreText.concatenatePaths(site.getURL(), url);
}
if (url && handlerSchema.styles?.version) {
// Add the version to the URL to prevent getting a cached file.