diff --git a/scripts/langindex.json b/scripts/langindex.json index b269f6b62..1df9a198e 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -1073,6 +1073,12 @@ "addon.privatefiles.sitefiles": "moodle", "addon.qtype_essay.maxwordlimitboundary": "qtype_essay", "addon.qtype_essay.minwordlimitboundary": "qtype_essay", + "addon.report_insights.actionsaved": "report_insights", + "addon.report_insights.fixedack": "analytics", + "addon.report_insights.incorrectlyflagged": "analytics", + "addon.report_insights.notapplicable": "analytics", + "addon.report_insights.notuseful": "analytics", + "addon.report_insights.useful": "analytics", "addon.storagemanager.deletecourse": "local_moodlemobileapp", "addon.storagemanager.deletecourses": "local_moodlemobileapp", "addon.storagemanager.deletedatafrom": "local_moodlemobileapp", diff --git a/src/addons/addons.module.ts b/src/addons/addons.module.ts index d7fd185a5..e74659659 100644 --- a/src/addons/addons.module.ts +++ b/src/addons/addons.module.ts @@ -30,6 +30,7 @@ import { AddonPrivateFilesModule } from './privatefiles/privatefiles.module'; import { AddonQbehaviourModule } from './qbehaviour/qbehaviour.module'; import { AddonQtypeModule } from './qtype/qtype.module'; import { AddonRemoteThemesModule } from './remotethemes/remotethemes.module'; +import { AddonReportModule } from './report/report.module'; import { AddonStorageManagerModule } from './storagemanager/storagemanager.module'; import { AddonUserProfileFieldModule } from './userprofilefield/userprofilefield.module'; @@ -51,6 +52,7 @@ import { AddonUserProfileFieldModule } from './userprofilefield/userprofilefield AddonQbehaviourModule, AddonQtypeModule, AddonRemoteThemesModule, + AddonReportModule, AddonStorageManagerModule, AddonUserProfileFieldModule, ], diff --git a/src/addons/report/insights/insights.module.ts b/src/addons/report/insights/insights.module.ts new file mode 100644 index 000000000..cd4a43f9b --- /dev/null +++ b/src/addons/report/insights/insights.module.ts @@ -0,0 +1,33 @@ +// (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 { APP_INITIALIZER, NgModule } from '@angular/core'; + +import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate'; +import { AddonReportInsightsActionLinkHandler } from './services/handlers/action-link'; + +@NgModule({ + imports: [], + exports: [], + providers: [ + { + provide: APP_INITIALIZER, + multi: true, + useValue: () => { + CoreContentLinksDelegate.registerHandler(AddonReportInsightsActionLinkHandler.instance); + }, + }, + ], +}) +export class AddonReportInsightsModule {} diff --git a/src/addons/report/insights/lang.json b/src/addons/report/insights/lang.json new file mode 100644 index 000000000..9f2b8b7b4 --- /dev/null +++ b/src/addons/report/insights/lang.json @@ -0,0 +1,8 @@ +{ + "actionsaved": "Your feedback of '{{$a}}' has been saved", + "fixedack": "Accept", + "incorrectlyflagged": "Incorrectly flagged", + "notapplicable": "Not applicable", + "notuseful": "Not useful", + "useful": "Useful" +} diff --git a/src/addons/report/insights/services/handlers/action-link.ts b/src/addons/report/insights/services/handlers/action-link.ts new file mode 100644 index 000000000..aad2a84fc --- /dev/null +++ b/src/addons/report/insights/services/handlers/action-link.ts @@ -0,0 +1,104 @@ +// (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 { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler'; +import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate'; +import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper'; +import { CoreSites } from '@services/sites'; +import { CoreDomUtils } from '@services/utils/dom'; +import { makeSingleton, Translate } from '@singletons'; +import { AddonReportInsights } from '../insights'; + +// Bulk actions supported, along with the related lang string. +const BULK_ACTIONS = { + fixed: 'addon.report_insights.fixedack', + incorrectlyflagged: 'addon.report_insights.incorrectlyflagged', + notapplicable: 'addon.report_insights.notapplicable', + notuseful: 'addon.report_insights.notuseful', + useful: 'addon.report_insights.useful', +}; + +/** + * Content links handler for calendar view page. + */ +@Injectable({ providedIn: 'root' }) +export class AddonReportInsightsActionLinkHandlerService extends CoreContentLinksHandlerBase { + + name = 'AddonReportInsightsActionLinkHandler'; + pattern = /\/report\/insights\/action\.php/; + + /** + * @inheritdoc + */ + getActions( + siteIds: string[], + url: string, + params: Record, + ): CoreContentLinksAction[] | Promise { + return [{ + action: async (siteId?: string): Promise => { + // Send the action. + const modal = await CoreDomUtils.showModalLoading('core.sending', true); + + try { + await AddonReportInsights.sendActionExecuted(params.action, [Number(params.predictionid)], siteId); + } catch (error) { + CoreDomUtils.showErrorModal(error); + + return; + } finally { + modal.dismiss(); + } + + if (BULK_ACTIONS[params.action]) { + // Done, display a toast. + CoreDomUtils.showToast(Translate.instant('addon.report_insights.actionsaved', { + $a: Translate.instant(BULK_ACTIONS[params.action]), + })); + } else if (!params.forwardurl) { + // Forward URL not defined, display a toast. + CoreDomUtils.showToast('core.success', true); + } else { + // Try to open the link in the app. + const forwardUrl = decodeURIComponent(params.forwardurl); + + const treated = await CoreContentLinksHelper.handleLink(forwardUrl); + if (!treated) { + // Cannot be opened in the app, open in browser. + const site = await CoreSites.getSite(siteId); + + await site.openInBrowserWithAutoLoginIfSameSite(forwardUrl); + } + } + }, + }]; + } + + /** + * @inheritdoc + */ + async isEnabled(siteId: string, url: string, params: Record): Promise { + if (!params.action || !params.predictionid) { + // Required params missing. + return false; + } + + return await AddonReportInsights.canSendActionInSite(siteId); + } + +} + +export const AddonReportInsightsActionLinkHandler = makeSingleton(AddonReportInsightsActionLinkHandlerService); diff --git a/src/addons/report/insights/services/insights.ts b/src/addons/report/insights/services/insights.ts new file mode 100644 index 000000000..003a18612 --- /dev/null +++ b/src/addons/report/insights/services/insights.ts @@ -0,0 +1,93 @@ +// (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 { CoreSites } from '@services/sites'; +import { CoreSite } from '@classes/site'; +import { makeSingleton } from '@singletons'; +import { CoreWSExternalWarning } from '@services/ws'; +import { CoreWSError } from '@classes/errors/wserror'; + +/** + * Service to handle insights. + */ +@Injectable({ providedIn: 'root' }) +export class AddonReportInsightsService { + + /** + * Check if site supports sending insight actions. + * + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved with boolean: whether it's supported. + */ + async canSendActionInSite(siteId?: string): Promise { + const site = await CoreSites.getSite(siteId); + + return this.canSendAction(site); + } + + /** + * Check if site supports sending insight actions. + * + * @param site Site. If not defined, current site. + * @return Whether it's supported. + */ + canSendAction(site?: CoreSite): boolean { + site = site || CoreSites.getCurrentSite(); + + return !!site?.wsAvailable('report_insights_action_executed'); + } + + /** + * Send an action. + * + * @param actionName Action name. + * @param ids List of IDs. + * @param siteId Site ID. If not defined, current site. + * @return Promise resolved if success. + */ + async sendActionExecuted(actionName: string, ids: number[], siteId?: string): Promise { + const site = await CoreSites.getSite(siteId); + + const params: AddonReportInsightsActionExecutedWSParams = { + actionname: actionName, + predictionids: ids, + }; + + const result = await site.write('report_insights_action_executed', params); + + if (result.warnings?.length) { + throw new CoreWSError(result.warnings[0]); + } + } + +} + +export const AddonReportInsights = makeSingleton(AddonReportInsightsService); + +/** + * Params of WS report_insights_action_executed. + */ +export type AddonReportInsightsActionExecutedWSParams = { + actionname: string; // The name of the action. + predictionids: number[]; // Array of prediction ids. +}; + +/** + * Result of WS report_insights_action_executed. + */ +export type AddonReportInsightsActionExecutedWSResult = { + warnings?: CoreWSExternalWarning[]; +}; diff --git a/src/addons/report/report.module.ts b/src/addons/report/report.module.ts new file mode 100644 index 000000000..e544caede --- /dev/null +++ b/src/addons/report/report.module.ts @@ -0,0 +1,23 @@ +// (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 { NgModule } from '@angular/core'; +import { AddonReportInsightsModule } from './insights/insights.module'; + +@NgModule({ + imports: [ + AddonReportInsightsModule, + ], +}) +export class AddonReportModule { }