commit
7f987f318c
|
@ -21,7 +21,9 @@ const { mkdirSync, copySync } = require('fs-extra');
|
||||||
const { resolve, extname, dirname, basename, relative } = require('path');
|
const { resolve, extname, dirname, basename, relative } = require('path');
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const pluginPath = process.argv[2] || guessPluginPath() || fail('Folder argument missing!');
|
const pluginPath = process.argv[2]
|
||||||
|
|| guessPluginPath()
|
||||||
|
|| fail('Folder argument missing! (you can also set the MOODLE_APP_BEHAT_PLUGIN_PATH env variable)');
|
||||||
const excludeFeatures = process.argv.some(arg => arg === '--exclude-features');
|
const excludeFeatures = process.argv.some(arg => arg === '--exclude-features');
|
||||||
const exclusions = excludeFeatures
|
const exclusions = excludeFeatures
|
||||||
? [
|
? [
|
||||||
|
|
|
@ -14,6 +14,12 @@ function get_app_version {
|
||||||
return
|
return
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if ! command -v jq &> /dev/null
|
||||||
|
then
|
||||||
|
echo "You need to install the jq program in order to run this command"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
APP_VERSION=$(jq -r '.versionname' ../moodle.config.json| cut -d. -f1-2)
|
APP_VERSION=$(jq -r '.versionname' ../moodle.config.json| cut -d. -f1-2)
|
||||||
if [ ! -z "$APP_VERSION" ]; then
|
if [ ! -z "$APP_VERSION" ]; then
|
||||||
export LANGVERSION=$APP_VERSION
|
export LANGVERSION=$APP_VERSION
|
||||||
|
|
|
@ -338,9 +338,9 @@
|
||||||
"addon.mod_assign.addnewattempt": "assign",
|
"addon.mod_assign.addnewattempt": "assign",
|
||||||
"addon.mod_assign.addnewattemptfromprevious": "assign",
|
"addon.mod_assign.addnewattemptfromprevious": "assign",
|
||||||
"addon.mod_assign.addsubmission": "assign",
|
"addon.mod_assign.addsubmission": "assign",
|
||||||
"addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "assign",
|
"addon.mod_assign.allowsubmissionsanddescriptionfromdatesummary": "local_moodlemobileapp",
|
||||||
"addon.mod_assign.allowsubmissionsfromdate": "assign",
|
"addon.mod_assign.allowsubmissionsfromdate": "assign",
|
||||||
"addon.mod_assign.allowsubmissionsfromdatesummary": "assign",
|
"addon.mod_assign.allowsubmissionsfromdatesummary": "local_moodlemobileapp",
|
||||||
"addon.mod_assign.applytoteam": "assign",
|
"addon.mod_assign.applytoteam": "assign",
|
||||||
"addon.mod_assign.assignmentisdue": "assign",
|
"addon.mod_assign.assignmentisdue": "assign",
|
||||||
"addon.mod_assign.assigntimeleft": "assign",
|
"addon.mod_assign.assigntimeleft": "assign",
|
||||||
|
|
|
@ -40,6 +40,8 @@ export class CoreBlockSideBlocksButtonComponent implements OnInit, OnDestroy {
|
||||||
component: CoreBlockSideBlocksTourComponent,
|
component: CoreBlockSideBlocksTourComponent,
|
||||||
side: CoreUserToursSide.Start,
|
side: CoreUserToursSide.Start,
|
||||||
alignment: CoreUserToursAlignment.Center,
|
alignment: CoreUserToursAlignment.Center,
|
||||||
|
after: 'user-menu',
|
||||||
|
afterTimeout: 1000,
|
||||||
getFocusedElement: nativeButton => {
|
getFocusedElement: nativeButton => {
|
||||||
const innerButton = Array.from(nativeButton.shadowRoot?.children ?? []).find(child => child.tagName === 'BUTTON');
|
const innerButton = Array.from(nativeButton.shadowRoot?.children ?? []).find(child => child.tagName === 'BUTTON');
|
||||||
|
|
||||||
|
|
|
@ -493,7 +493,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
|
||||||
* Log activity view in analytics.
|
* Log activity view in analytics.
|
||||||
*
|
*
|
||||||
* @param wsName Name of the WS used.
|
* @param wsName Name of the WS used.
|
||||||
* @param data Other data to send.
|
* @param options Other data to send.
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
async analyticsLogEvent(
|
async analyticsLogEvent(
|
||||||
|
|
|
@ -26,6 +26,7 @@ import { CoreDom } from '@singletons/dom';
|
||||||
import { CoreSubscriptions } from '@singletons/subscriptions';
|
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||||
import { CoreUserToursUserTourComponent } from '../components/user-tour/user-tour';
|
import { CoreUserToursUserTourComponent } from '../components/user-tour/user-tour';
|
||||||
import { APP_SCHEMA, CoreUserToursDBEntry, USER_TOURS_TABLE_NAME } from './database/user-tours';
|
import { APP_SCHEMA, CoreUserToursDBEntry, USER_TOURS_TABLE_NAME } from './database/user-tours';
|
||||||
|
import { CorePromisedValue } from '@classes/promised-value';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service to manage User Tours.
|
* Service to manage User Tours.
|
||||||
|
@ -35,6 +36,7 @@ export class CoreUserToursService {
|
||||||
|
|
||||||
protected table = asyncInstance<CoreDatabaseTable<CoreUserToursDBEntry>>();
|
protected table = asyncInstance<CoreDatabaseTable<CoreUserToursDBEntry>>();
|
||||||
protected tours: { component: CoreUserToursUserTourComponent; visible: boolean }[] = [];
|
protected tours: { component: CoreUserToursUserTourComponent; visible: boolean }[] = [];
|
||||||
|
protected toursListeners: Partial<Record<string, CorePromisedValue<void>[]>> = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize database.
|
* Initialize database.
|
||||||
|
@ -114,6 +116,8 @@ export class CoreUserToursService {
|
||||||
|
|
||||||
await CoreUtils.wait(delay ?? 200);
|
await CoreUtils.wait(delay ?? 200);
|
||||||
|
|
||||||
|
options.after && await this.waitForUserTour(options.after, options.afterTimeout);
|
||||||
|
|
||||||
const container = document.querySelector('ion-app') ?? document.body;
|
const container = document.querySelector('ion-app') ?? document.body;
|
||||||
const viewContainer = container.querySelector('ion-router-outlet, ion-nav, #ion-view-container-root');
|
const viewContainer = container.querySelector('ion-router-outlet, ion-nav, #ion-view-container-root');
|
||||||
const element = await AngularFrameworkDelegate.attachViewToDom(
|
const element = await AngularFrameworkDelegate.attachViewToDom(
|
||||||
|
@ -125,6 +129,8 @@ export class CoreUserToursService {
|
||||||
|
|
||||||
viewContainer?.setAttribute('aria-hidden', 'true');
|
viewContainer?.setAttribute('aria-hidden', 'true');
|
||||||
|
|
||||||
|
this.toursListeners[options.id]?.forEach(listener => listener.resolve());
|
||||||
|
|
||||||
return this.startTour(tour, options.watch ?? (options as CoreUserToursFocusedOptions).focus);
|
return this.startTour(tour, options.watch ?? (options as CoreUserToursFocusedOptions).focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -298,7 +304,7 @@ export class CoreUserToursService {
|
||||||
* Is user Tour disabled?
|
* Is user Tour disabled?
|
||||||
*
|
*
|
||||||
* @param tourId Tour Id or undefined to check all user tours.
|
* @param tourId Tour Id or undefined to check all user tours.
|
||||||
* @returns Wether a particular or all user tours are disabled.
|
* @returns Whether a particular or all user tours are disabled.
|
||||||
*/
|
*/
|
||||||
isDisabled(tourId?: string): boolean {
|
isDisabled(tourId?: string): boolean {
|
||||||
if (CoreConstants.CONFIG.disableUserTours) {
|
if (CoreConstants.CONFIG.disableUserTours) {
|
||||||
|
@ -319,6 +325,30 @@ export class CoreUserToursService {
|
||||||
await this.table.delete();
|
await this.table.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for another user tour to be shown.
|
||||||
|
*
|
||||||
|
* @param id Id of the user tour to wait for.
|
||||||
|
* @param timeout Timeout after which the waiting will end regardless of the user tour having been shown or not.
|
||||||
|
*/
|
||||||
|
async waitForUserTour(id: string, timeout?: number): Promise<void> {
|
||||||
|
const listener = new CorePromisedValue<void>();
|
||||||
|
const listeners = this.toursListeners[id] = this.toursListeners[id] ?? [];
|
||||||
|
const removeListener = () => {
|
||||||
|
const index = listeners.indexOf(listener);
|
||||||
|
|
||||||
|
if (index !== -1) {
|
||||||
|
listeners.splice(index, 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
listeners.push(listener);
|
||||||
|
listener.then(removeListener).catch(removeListener);
|
||||||
|
timeout && setTimeout(() => listener.resolve(), timeout);
|
||||||
|
|
||||||
|
await listener;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const CoreUserTours = makeSingleton(CoreUserToursService);
|
export const CoreUserTours = makeSingleton(CoreUserToursService);
|
||||||
|
@ -329,8 +359,8 @@ export const CoreUserTours = makeSingleton(CoreUserToursService);
|
||||||
export interface CoreUserToursUserTour {
|
export interface CoreUserToursUserTour {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancelling a User Tours removed it from the queue if it was pending or dimisses it without
|
* Cancelling a User Tour removes it from the queue if it was pending or dismisses it without
|
||||||
* acknowledging if it existed.
|
* acknowledging.
|
||||||
*/
|
*/
|
||||||
cancel(): Promise<void>;
|
cancel(): Promise<void>;
|
||||||
|
|
||||||
|
@ -391,6 +421,16 @@ export interface CoreUserToursBasicOptions {
|
||||||
*/
|
*/
|
||||||
watch?: HTMLElement | false;
|
watch?: HTMLElement | false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show this user tour only after another user tour with the given id has been shown.
|
||||||
|
*/
|
||||||
|
after?: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether to show this user tour after the given timeout (in milliseconds) if the other user-tour hasn't been shown.
|
||||||
|
*/
|
||||||
|
afterTimeout?: number;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { CoreSites } from './sites';
|
||||||
import { CoreConfig, CoreConfigProvider } from './config';
|
import { CoreConfig, CoreConfigProvider } from './config';
|
||||||
import { CoreConstants } from '../constants';
|
import { CoreConstants } from '../constants';
|
||||||
import { CoreUrlUtils } from './utils/url';
|
import { CoreUrlUtils } from './utils/url';
|
||||||
|
import { CoreTextUtils } from '@services/utils/text';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper service to support analytics.
|
* Helper service to support analytics.
|
||||||
|
@ -77,6 +78,11 @@ export class CoreAnalyticsService extends CoreDelegate<CoreAnalyticsHandler> {
|
||||||
...event,
|
...event,
|
||||||
siteId: site.getId(),
|
siteId: site.getId(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (treatedEvent.type === CoreAnalyticsEventType.VIEW_ITEM || treatedEvent.type === CoreAnalyticsEventType.VIEW_ITEM_LIST) {
|
||||||
|
treatedEvent.name = CoreTextUtils.cleanTags(treatedEvent.name);
|
||||||
|
}
|
||||||
|
|
||||||
if ('url' in treatedEvent && treatedEvent.url) {
|
if ('url' in treatedEvent && treatedEvent.url) {
|
||||||
if (!CoreUrlUtils.isAbsoluteURL(treatedEvent.url)) {
|
if (!CoreUrlUtils.isAbsoluteURL(treatedEvent.url)) {
|
||||||
treatedEvent.url = site.createSiteUrl(treatedEvent.url);
|
treatedEvent.url = site.createSiteUrl(treatedEvent.url);
|
||||||
|
|
Loading…
Reference in New Issue