MOBILE-4059 mainmenu: Add link to contact support
parent
95e0640eb2
commit
4eb01a063c
|
@ -2363,6 +2363,7 @@
|
|||
"core.user.roles": "moodle",
|
||||
"core.user.sendemail": "local_moodlemobileapp",
|
||||
"core.user.student": "moodle/defaultcoursestudent",
|
||||
"core.user.support": "local_moodlemobileapp",
|
||||
"core.user.teacher": "moodle/noneditingteacher",
|
||||
"core.user.useraccount": "moodle",
|
||||
"core.user.userwithid": "local_moodlemobileapp",
|
||||
|
|
|
@ -60,6 +60,7 @@ import {
|
|||
import { Observable, ObservableInput, ObservedValueOf, OperatorFunction, Subject } from 'rxjs';
|
||||
import { finalize, map, mergeMap } from 'rxjs/operators';
|
||||
import { firstValueFrom } from '../utils/rxjs';
|
||||
import { CoreUserSupport } from '@features/user/services/support';
|
||||
|
||||
/**
|
||||
* QR Code type enumeration.
|
||||
|
@ -262,6 +263,19 @@ export class CoreSite {
|
|||
return this.db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get url to contact site support.
|
||||
*
|
||||
* @returns Site support page url.
|
||||
*/
|
||||
getSupportPageUrl(): string | null {
|
||||
if (!this.config || !this.canContactSupport()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return CoreUserSupport.getSupportPageUrl(this.config, this.siteUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get site user's ID.
|
||||
*
|
||||
|
@ -421,6 +435,19 @@ export class CoreSite {
|
|||
return !!(info && (info.usercanmanageownfiles === undefined || info.usercanmanageownfiles));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether this site has a support url available.
|
||||
*
|
||||
* @returns Whether this site has a support url.
|
||||
*/
|
||||
canContactSupport(): boolean {
|
||||
if (this.isFeatureDisabled('NoDelegate_CoreUserSupport')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return !!this.config && CoreUserSupport.canContactSupport(this.config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the user download files?
|
||||
*
|
||||
|
@ -2777,6 +2804,7 @@ export type CoreSitePublicConfigResponse = {
|
|||
agedigitalconsentverification?: boolean; // Whether age digital consent verification is enabled.
|
||||
supportname?: string; // Site support contact name (only if age verification is enabled).
|
||||
supportemail?: string; // Site support contact email (only if age verification is enabled).
|
||||
supportpage?: string; // Site support contact url.
|
||||
autolang?: number; // Whether to detect default language from browser setting.
|
||||
lang?: string; // Default language for the site.
|
||||
langmenu?: number; // Whether the language menu should be displayed.
|
||||
|
|
|
@ -66,6 +66,14 @@
|
|||
<p class="item-heading">{{ 'core.settings.preferences' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
|
||||
<ion-item *ngIf="displayContactSupport" button (click)="contactSupport($event)"
|
||||
[attr.aria-label]="'core.user.support' | translate" detail="true" detailIcon="open-outline" class="core-user-menu-support">
|
||||
<ion-icon name="fas-envelope" slot="start" aria-hidden="true"></ion-icon>
|
||||
<ion-label>
|
||||
<p class="item-heading">{{ 'core.user.support' | translate }}</p>
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -18,6 +18,7 @@ import { CoreSite, CoreSiteInfo } from '@classes/site';
|
|||
import { CoreFilter } from '@features/filter/services/filter';
|
||||
import { CoreLoginSitesComponent } from '@features/login/components/sites/sites';
|
||||
import { CoreLoginHelper } from '@features/login/services/login-helper';
|
||||
import { CoreUserSupport } from '@features/user/services/support';
|
||||
import { CoreUser, CoreUserProfile } from '@features/user/services/user';
|
||||
import {
|
||||
CoreUserProfileHandlerData,
|
||||
|
@ -51,6 +52,7 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy {
|
|||
handlersLoaded = false;
|
||||
user?: CoreUserProfile;
|
||||
displaySwitchAccount = true;
|
||||
displayContactSupport = false;
|
||||
removeAccountOnLogout = false;
|
||||
|
||||
protected subscription!: Subscription;
|
||||
|
@ -65,6 +67,7 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy {
|
|||
this.siteName = currentSite.getSiteName();
|
||||
this.siteUrl = currentSite.getURL();
|
||||
this.displaySwitchAccount = !currentSite.isFeatureDisabled('NoDelegate_SwitchAccount');
|
||||
this.displayContactSupport = currentSite.canContactSupport();
|
||||
this.removeAccountOnLogout = !!CoreConstants.CONFIG.removeaccountonlogout;
|
||||
|
||||
this.loadSiteLogo(currentSite);
|
||||
|
@ -173,6 +176,16 @@ export class CoreMainMenuUserMenuComponent implements OnInit, OnDestroy {
|
|||
handler.action(event, this.user, CoreUserDelegateContext.USER_MENU);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contact site support.
|
||||
*
|
||||
* @param event Click event.
|
||||
*/
|
||||
async contactSupport(event: Event): Promise<void> {
|
||||
await this.close(event);
|
||||
await CoreUserSupport.contact();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout the user.
|
||||
*
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"roles": "Roles",
|
||||
"sendemail": "Email",
|
||||
"student": "Student",
|
||||
"support": "Support",
|
||||
"teacher": "Non-editing teacher",
|
||||
"userwithid": "User with ID {{id}}",
|
||||
"webpage": "Web page"
|
||||
|
|
|
@ -0,0 +1,112 @@
|
|||
// (C) Copyright 2015 Moodle Pty Ltd.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreError } from '@classes/errors/error';
|
||||
import { CoreSiteConfig, CoreSitePublicConfigResponse } from '@classes/site';
|
||||
import { InAppBrowserObject } from '@ionic-native/in-app-browser';
|
||||
import { CorePlatform } from '@services/platform';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { makeSingleton } from '@singletons';
|
||||
import { CoreEvents } from '@singletons/events';
|
||||
import { CoreSubscriptions } from '@singletons/subscriptions';
|
||||
|
||||
/**
|
||||
* Handle site support.
|
||||
*/
|
||||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreUserSupportService {
|
||||
|
||||
/**
|
||||
* Contact site support.
|
||||
*
|
||||
* @param options Options to configure the interaction with support.
|
||||
*/
|
||||
async contact(options: CoreUserSupportContactOptions = {}): Promise<void> {
|
||||
const supportPageUrl = options.supportPageUrl ?? CoreSites.getRequiredCurrentSite().getSupportPageUrl();
|
||||
|
||||
if (!supportPageUrl) {
|
||||
throw new CoreError('Could not get support url');
|
||||
}
|
||||
|
||||
const autoLoginUrl = await CoreSites.getCurrentSite()?.getAutoLoginUrl(supportPageUrl, false);
|
||||
const browser = CoreUtils.openInApp(autoLoginUrl ?? supportPageUrl);
|
||||
|
||||
if (supportPageUrl.endsWith('/user/contactsitesupport.php')) {
|
||||
this.populateSupportForm(browser, options.subject, options.message);
|
||||
}
|
||||
|
||||
await CoreEvents.waitUntil(CoreEvents.IAB_EXIT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get support page url from site config.
|
||||
*
|
||||
* @param config Site config.
|
||||
* @returns Support page url.
|
||||
*/
|
||||
getSupportPageUrl(config: CoreSitePublicConfigResponse): string;
|
||||
getSupportPageUrl(config: CoreSiteConfig, siteUrl: string): string;
|
||||
getSupportPageUrl(config: CoreSiteConfig | CoreSitePublicConfigResponse, siteUrl?: string): string {
|
||||
return config.supportpage?.trim()
|
||||
|| `${config.httpswwwroot ?? config.wwwroot ?? siteUrl}/user/contactsitesupport.php`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether a site config allows contacting support.
|
||||
*
|
||||
* @param config Site config.
|
||||
* @returns Whether site support can be contacted.
|
||||
*/
|
||||
canContactSupport(config: CoreSiteConfig | CoreSitePublicConfigResponse): boolean {
|
||||
return 'supportpage' in config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject error details into contact support form.
|
||||
*
|
||||
* @param browser In App browser containing the support form.
|
||||
* @param subject Title to fill into the form.
|
||||
* @param message Details to fill into the form.
|
||||
*/
|
||||
protected populateSupportForm(browser: InAppBrowserObject, subject?: string | null, message?: string | null): void {
|
||||
if (!CorePlatform.isMobile()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const unsubscribe = CoreSubscriptions.once(browser.on('loadstop'), () => {
|
||||
browser.executeScript({
|
||||
code: `
|
||||
document.querySelector('#id_subject').value = ${JSON.stringify(subject ?? '')};
|
||||
document.querySelector('#id_message').value = ${JSON.stringify(message ?? '')};
|
||||
`,
|
||||
});
|
||||
});
|
||||
|
||||
CoreEvents.once(CoreEvents.IAB_EXIT, () => unsubscribe());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreUserSupport = makeSingleton(CoreUserSupportService);
|
||||
|
||||
/**
|
||||
* Options to configure interaction with support.
|
||||
*/
|
||||
export interface CoreUserSupportContactOptions {
|
||||
supportPageUrl?: string | null;
|
||||
subject?: string | null;
|
||||
message?: string | null;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
@core @core_user @app @javascript @lms_upto3.11
|
||||
Feature: Site support
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname |
|
||||
| student1 | Student | Student |
|
||||
|
||||
Scenario: Cannot contact support
|
||||
Given I entered the app as "student1"
|
||||
When I press the user menu button in the app
|
||||
Then I should find "Blog entries" in the app
|
||||
But I should not find "Support" in the app
|
|
@ -0,0 +1,33 @@
|
|||
@core @core_user @app @javascript @lms_from4.0
|
||||
Feature: Site support
|
||||
|
||||
Background:
|
||||
Given the following "users" exist:
|
||||
| username | firstname | lastname |
|
||||
| student1 | Student | Student |
|
||||
|
||||
Scenario: Uses default support page
|
||||
Given I entered the app as "student1"
|
||||
When I press the user menu button in the app
|
||||
Then I should find "Support" in the app
|
||||
|
||||
When I press "Support" in the app
|
||||
Then the app should have opened a browser tab with url ".*\/user\/contactsitesupport\.php"
|
||||
|
||||
Scenario: Uses custom support page
|
||||
Given the following config values are set as admin:
|
||||
| supportpage | https://campus.example.edu/support |
|
||||
And I entered the app as "student1"
|
||||
When I press the user menu button in the app
|
||||
Then I should find "Support" in the app
|
||||
|
||||
When I press "Support" in the app
|
||||
Then the app should have opened a browser tab with url "https:\/\/campus\.example\.edu\/support"
|
||||
|
||||
Scenario: Cannot contact support
|
||||
Given the following config values are set as admin:
|
||||
| disabledfeatures | NoDelegate_CoreUserSupport | tool_mobile |
|
||||
And I entered the app as "student1"
|
||||
When I press the user menu button in the app
|
||||
Then I should find "Blog entries" in the app
|
||||
But I should not find "Support" in the app
|
|
@ -1044,11 +1044,7 @@ export class CoreUtilsProvider {
|
|||
* @param options Override default options passed to InAppBrowser.
|
||||
* @return The opened window.
|
||||
*/
|
||||
openInApp(url: string, options?: InAppBrowserOptions): InAppBrowserObject | undefined {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
openInApp(url: string, options?: InAppBrowserOptions): InAppBrowserObject {
|
||||
options = options || {};
|
||||
options.usewkwebview = 'yes'; // Force WKWebView in iOS.
|
||||
options.enableViewPortScale = options.enableViewPortScale ?? 'yes'; // Enable zoom on iOS by default.
|
||||
|
|
|
@ -282,6 +282,15 @@ export class CoreEvents {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait until an event has been emitted.
|
||||
*
|
||||
* @param eventName Event name.
|
||||
*/
|
||||
static waitUntil(eventName: string): Promise<void> {
|
||||
return new Promise(resolve => this.once(eventName, () => resolve()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue