Merge pull request #3629 from alfonso-salces/MOBILE-4248

Mobile 4248
main
Dani Palou 2023-04-25 10:49:18 +02:00 committed by GitHub
commit 882cbb6473
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 156 additions and 13 deletions

View File

@ -524,6 +524,7 @@
"addon.mod_choice.savemychoice": "choice", "addon.mod_choice.savemychoice": "choice",
"addon.mod_choice.userchoosethisoption": "choice", "addon.mod_choice.userchoosethisoption": "choice",
"addon.mod_choice.yourselection": "choice", "addon.mod_choice.yourselection": "choice",
"addon.mod_data.actions": "data",
"addon.mod_data.addentries": "data", "addon.mod_data.addentries": "data",
"addon.mod_data.advancedsearch": "data", "addon.mod_data.advancedsearch": "data",
"addon.mod_data.alttext": "data", "addon.mod_data.alttext": "data",
@ -567,6 +568,7 @@
"addon.mod_data.search": "data", "addon.mod_data.search": "data",
"addon.mod_data.searchbytagsnotsupported": "local_moodlemobileapp", "addon.mod_data.searchbytagsnotsupported": "local_moodlemobileapp",
"addon.mod_data.selectedrequired": "data", "addon.mod_data.selectedrequired": "data",
"addon.mod_data.showmore": "data",
"addon.mod_data.single": "data", "addon.mod_data.single": "data",
"addon.mod_data.tagarea_data_records": "data", "addon.mod_data.tagarea_data_records": "data",
"addon.mod_data.timeadded": "data", "addon.mod_data.timeadded": "data",

View File

@ -23,12 +23,15 @@ import {
AddonModDataAction, AddonModDataAction,
AddonModDataData, AddonModDataData,
AddonModDataEntry, AddonModDataEntry,
AddonModDataGetDataAccessInformationWSResponse,
AddonModDataProvider, AddonModDataProvider,
AddonModDataTemplateMode, AddonModDataTemplateMode,
} from '../../services/data'; } from '../../services/data';
import { AddonModDataHelper } from '../../services/data-helper'; import { AddonModDataHelper } from '../../services/data-helper';
import { AddonModDataOffline } from '../../services/data-offline'; import { AddonModDataOffline } from '../../services/data-offline';
import { AddonModDataModuleHandlerService } from '../../services/handlers/module'; import { AddonModDataModuleHandlerService } from '../../services/handlers/module';
import { CoreDomUtils } from '@services/utils/dom';
import { AddonModDataActionsMenuComponent, AddonModDataActionsMenuItem } from '../actionsmenu/actionsmenu';
/** /**
* Component that displays a database action. * Component that displays a database action.
@ -39,6 +42,7 @@ import { AddonModDataModuleHandlerService } from '../../services/handlers/module
}) })
export class AddonModDataActionComponent implements OnInit { export class AddonModDataActionComponent implements OnInit {
@Input() access?: AddonModDataGetDataAccessInformationWSResponse; // Access info.
@Input() mode!: AddonModDataTemplateMode; // The render mode. @Input() mode!: AddonModDataTemplateMode; // The render mode.
@Input() action!: AddonModDataAction; // The field to render. @Input() action!: AddonModDataAction; // The field to render.
@Input() entry!: AddonModDataEntry; // The value of the field. @Input() entry!: AddonModDataEntry; // The value of the field.
@ -139,4 +143,66 @@ export class AddonModDataActionComponent implements OnInit {
CoreEvents.trigger(AddonModDataProvider.ENTRY_CHANGED, { dataId: dataId, entryId: entryId }, this.siteId); CoreEvents.trigger(AddonModDataProvider.ENTRY_CHANGED, { dataId: dataId, entryId: entryId }, this.siteId);
} }
/**
* Open actions menu popover.
*/
async actionsMenu(): Promise<void> {
const items: AddonModDataActionsMenuItem[] = [];
if (this.entry.canmanageentry) {
items.push(
this.entry.deleted
? {
action: () => this.undoDelete(),
text: 'core.restore',
icon: 'fas-rotate-left',
}
: {
action: () => this.deleteEntry(),
text: 'core.delete',
icon: 'fas-trash',
},
);
if (!this.entry.deleted) {
items.unshift({
action: () => this.editEntry(),
text: 'core.edit',
icon: 'fas-pen',
});
}
}
if (this.database.approval && this.access?.canapprove && !this.entry.deleted) {
items.push(
!this.entry.approved
? {
action: () => this.approveEntry(),
text: 'addon.mod_data.approve',
icon: 'fas-thumbs-up',
}
: {
action: () => this.disapproveEntry(),
text: 'addon.mod_data.disapprove',
icon: 'far-thumbs-down',
},
);
}
if (this.mode === AddonModDataTemplateMode.LIST) {
items.unshift({
action: () => this.viewEntry(),
text: 'addon.mod_data.showmore',
icon: 'fas-magnifying-glass-plus',
});
}
await CoreDomUtils.openPopover({
component: AddonModDataActionsMenuComponent,
componentProps: { items },
showBackdrop: true,
id: 'actionsmenu-popover',
});
}
} }

View File

@ -1,4 +1,10 @@
<ion-button size="small" *ngIf="action == 'more'" fill="clear" (click)="viewEntry()" [attr.aria-label]="'addon.mod_data.more' | translate"> <ion-button size="small" *ngIf="action == 'actionsmenu'" fill="clear" (click)="actionsMenu()"
[attr.aria-label]="'addon.mod_data.actions' | translate">
<ion-icon name="fas-ellipsis-vertical" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button>
<ion-button size="small" *ngIf="action == 'more'" fill="clear" (click)="viewEntry()"
[attr.aria-label]="'addon.mod_data.showmore' | translate">
<ion-icon name="fas-magnifying-glass-plus" slot="icon-only" aria-hidden="true"></ion-icon> <ion-icon name="fas-magnifying-glass-plus" slot="icon-only" aria-hidden="true"></ion-icon>
</ion-button> </ion-button>

View File

@ -0,0 +1,9 @@
<ion-list>
<ion-item button class="ion-text-wrap" (click)="onItemClick(item)" *ngFor="let item of items" detail="false"
[attr.aria-label]="item.text | translate">
<ion-label>
<p class="item-heading">{{ item.text | translate }}</p>
</ion-label>
<ion-icon [name]="item.icon" slot="end" aria-hidden="true"></ion-icon>
</ion-item>
</ion-list>

View File

@ -0,0 +1,45 @@
// (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 { Component, Input } from '@angular/core';
import { PopoverController } from '@singletons';
/**
* Component that displays the actionsmenu.
*/
@Component({
selector: 'addon-mod-data-actionsmenu',
templateUrl: 'actionsmenu.html',
})
export class AddonModDataActionsMenuComponent {
@Input() items: AddonModDataActionsMenuItem[] = [];
/**
* Execute item action and dismiss the popover.
*
* @param item item from which the action will be executed.
*/
async onItemClick(item: AddonModDataActionsMenuItem): Promise<void> {
item.action();
await PopoverController.dismiss();
}
}
export interface AddonModDataActionsMenuItem {
text: string;
icon: string;
action: () => void;
};

View File

@ -18,11 +18,13 @@ import { CoreCourseComponentsModule } from '@features/course/components/componen
import { AddonModDataIndexComponent } from './index'; import { AddonModDataIndexComponent } from './index';
import { AddonModDataSearchComponent } from './search/search'; import { AddonModDataSearchComponent } from './search/search';
import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module'; import { CoreCompileHtmlComponentModule } from '@features/compile/components/compile-html/compile-html.module';
import { AddonModDataActionsMenuComponent } from './actionsmenu/actionsmenu';
@NgModule({ @NgModule({
declarations: [ declarations: [
AddonModDataIndexComponent, AddonModDataIndexComponent,
AddonModDataSearchComponent, AddonModDataSearchComponent,
AddonModDataActionsMenuComponent,
], ],
imports: [ imports: [
CoreSharedModule, CoreSharedModule,
@ -32,6 +34,7 @@ import { CoreCompileHtmlComponentModule } from '@features/compile/components/com
exports: [ exports: [
AddonModDataIndexComponent, AddonModDataIndexComponent,
AddonModDataSearchComponent, AddonModDataSearchComponent,
AddonModDataActionsMenuComponent,
], ],
}) })
export class AddonModDataComponentsModule {} export class AddonModDataComponentsModule {}

View File

@ -95,6 +95,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
database: AddonModDataData; database: AddonModDataData;
title: string; title: string;
group: number; group: number;
access: AddonModDataGetDataAccessInformationWSResponse | undefined;
gotoEntry: (entryId: number) => void; gotoEntry: (entryId: number) => void;
}; };
@ -348,7 +349,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
this.entries.forEach((entry, index) => { this.entries.forEach((entry, index) => {
entriesById[entry.id] = entry; entriesById[entry.id] = entry;
const actions = AddonModDataHelper.getActions(this.database!, this.access!, entry); const actions = AddonModDataHelper.getActions(this.database!, this.access!, entry, AddonModDataTemplateMode.LIST);
const options: AddonModDatDisplayFieldsOptions = {}; const options: AddonModDatDisplayFieldsOptions = {};
if (!this.search.searching) { if (!this.search.searching) {
options.offset = this.search.page * AddonModDataProvider.PER_PAGE + index - numOfflineEntries; options.offset = this.search.page * AddonModDataProvider.PER_PAGE + index - numOfflineEntries;
@ -375,6 +376,7 @@ export class AddonModDataIndexComponent extends CoreCourseModuleMainActivityComp
database: this.database!, database: this.database!,
title: this.module.name, title: this.module.name,
group: this.selectedGroup, group: this.selectedGroup,
access: this.access,
gotoEntry: (entryId) => this.gotoEntry(entryId), gotoEntry: (entryId) => this.gotoEntry(entryId),
}; };
} else if (!this.search.searching) { } else if (!this.search.searching) {

View File

@ -1,4 +1,5 @@
{ {
"actions": "Actions menu",
"addentries": "Add entries", "addentries": "Add entries",
"advancedsearch": "Advanced search", "advancedsearch": "Advanced search",
"alttext": "Alternative text", "alttext": "Alternative text",
@ -42,6 +43,7 @@
"search": "Search", "search": "Search",
"searchbytagsnotsupported": "Sorry, searching by tags is not supported by the app.", "searchbytagsnotsupported": "Sorry, searching by tags is not supported by the app.",
"selectedrequired": "All selected required", "selectedrequired": "All selected required",
"showmore": "Show more",
"single": "View single", "single": "View single",
"tagarea_data_records": "Data records", "tagarea_data_records": "Data records",
"timeadded": "Time added", "timeadded": "Time added",

View File

@ -86,6 +86,7 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy {
database: AddonModDataData; database: AddonModDataData;
title: string; title: string;
group: number; group: number;
access: AddonModDataGetDataAccessInformationWSResponse | undefined;
}; };
ratingInfo?: CoreRatingInfo; ratingInfo?: CoreRatingInfo;
@ -187,7 +188,7 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy {
this.selectedGroup = CoreGroups.validateGroupId(this.selectedGroup, this.groupInfo); this.selectedGroup = CoreGroups.validateGroupId(this.selectedGroup, this.groupInfo);
const actions = AddonModDataHelper.getActions(this.database, this.access, this.entry!); const actions = AddonModDataHelper.getActions(this.database, this.access, this.entry!, AddonModDataTemplateMode.SHOW);
const template = AddonModDataHelper.getTemplate(this.database, AddonModDataTemplateType.SINGLE, this.fieldsArray); const template = AddonModDataHelper.getTemplate(this.database, AddonModDataTemplateType.SINGLE, this.fieldsArray);
this.entryHtml = AddonModDataHelper.displayShowFields( this.entryHtml = AddonModDataHelper.displayShowFields(
@ -215,6 +216,7 @@ export class AddonModDataEntryPage implements OnInit, OnDestroy {
database: this.database, database: this.database,
title: this.title, title: this.title,
group: this.selectedGroup, group: this.selectedGroup,
access: this.access,
}; };
if (this.logAfterFetch) { if (this.logAfterFetch) {

View File

@ -234,7 +234,7 @@ export class AddonModDataHelperProvider {
render = Translate.instant('addon.mod_data.' + (entry.approved ? 'approved' : 'notapproved')); render = Translate.instant('addon.mod_data.' + (entry.approved ? 'approved' : 'notapproved'));
} else { } else {
render = `<addon-mod-data-action action="${action}" [entry]="entries[${entry.id}]" mode="${mode}" ` + render = `<addon-mod-data-action action="${action}" [entry]="entries[${entry.id}]" mode="${mode}" ` +
'[database]="database" [title]="title" ' + '[database]="database" [access]="access" [title]="title" ' +
(options.offset !== undefined ? `[offset]="${options.offset}" ` : '') + (options.offset !== undefined ? `[offset]="${options.offset}" ` : '') +
(options.sortBy !== undefined ? `[sortBy]="${options.sortBy}" ` : '') + (options.sortBy !== undefined ? `[sortBy]="${options.sortBy}" ` : '') +
(options.sortDirection !== undefined ? `sortDirection="${options.sortDirection}" ` : '') + (options.sortDirection !== undefined ? `sortDirection="${options.sortDirection}" ` : '') +
@ -407,6 +407,7 @@ export class AddonModDataHelperProvider {
database: AddonModDataData, database: AddonModDataData,
accessInfo: AddonModDataGetDataAccessInformationWSResponse, accessInfo: AddonModDataGetDataAccessInformationWSResponse,
entry: AddonModDataEntry, entry: AddonModDataEntry,
mode: AddonModDataTemplateMode,
): Record<AddonModDataAction, boolean> { ): Record<AddonModDataAction, boolean> {
return { return {
add: false, // Not directly used on entries. add: false, // Not directly used on entries.
@ -426,6 +427,10 @@ export class AddonModDataHelperProvider {
approvalstatus: database.approval, approvalstatus: database.approval,
comments: database.comments, comments: database.comments,
actionsmenu: entry.canmanageentry
|| (database.approval && accessInfo.canapprove && !entry.deleted)
|| mode === AddonModDataTemplateMode.LIST,
// Unsupported actions. // Unsupported actions.
delcheck: false, delcheck: false,
export: false, export: false,
@ -497,7 +502,7 @@ export class AddonModDataHelperProvider {
html.push( html.push(
'<tr class="lastrow">', '<tr class="lastrow">',
'<td class="controls template-field cell c0 lastcol" style="" colspan="2">', '<td class="controls template-field cell c0 lastcol" style="" colspan="2">',
'##edit## ##more## ##delete## ##approve## ##disapprove## ##export##', '##actionsmenu## ##edit## ##more## ##delete## ##approve## ##disapprove## ##export##',
'</td>', '</td>',
'</tr>', '</tr>',
); );
@ -505,7 +510,7 @@ export class AddonModDataHelperProvider {
html.push( html.push(
'<tr class="lastrow">', '<tr class="lastrow">',
'<td class="controls template-field cell c0 lastcol" style="" colspan="2">', '<td class="controls template-field cell c0 lastcol" style="" colspan="2">',
'##edit## ##delete## ##approve## ##disapprove## ##export##', '##actionsmenu## ##edit## ##delete## ##approve## ##disapprove## ##export##',
'</td>', '</td>',
'</tr>', '</tr>',
); );

View File

@ -62,6 +62,7 @@ export enum AddonModDataAction {
APPROVALSTATUS = 'approvalstatus', APPROVALSTATUS = 'approvalstatus',
DELCHECK = 'delcheck', // Unused. DELCHECK = 'delcheck', // Unused.
EXPORT = 'export', // Unused. EXPORT = 'export', // Unused.
ACTIONSMENU = 'actionsmenu',
} }
export enum AddonModDataTemplateType { export enum AddonModDataTemplateType {

View File

@ -52,7 +52,7 @@ Feature: Users can manage entries in database activities
| URL | https://moodlecloud.com/ | | URL | https://moodlecloud.com/ |
| Description | Moodle Cloud | | Description | Moodle Cloud |
And I press "Save" near "Web links" in the app And I press "Save" near "Web links" in the app
And I press "More" near "Moodle community site" in the app And I press "Show more" near "Moodle community site" in the app
Then I should find "Moodle community site" in the app Then I should find "Moodle community site" in the app
And I should be able to press "Previous" in the app And I should be able to press "Previous" in the app
But I should not be able to press "Next" in the app But I should not be able to press "Next" in the app
@ -81,7 +81,7 @@ Feature: Users can manage entries in database activities
When I press "Web links" near "General" in the app When I press "Web links" near "General" in the app
Then "Edit" "link" should not exist Then "Edit" "link" should not exist
And "Delete" "link" should not exist And "Delete" "link" should not exist
And I press "More" in the app And I press "Show more" in the app
And "Edit" "link" should not exist And "Edit" "link" should not exist
And "Delete" "link" should not exist And "Delete" "link" should not exist
@ -122,7 +122,7 @@ Feature: Users can manage entries in database activities
And I press "Save" near "Web links" in the app And I press "Save" near "Web links" in the app
# Edit the entry from single view. # Edit the entry from single view.
When I press "More" in the app When I press "Show more" in the app
And I press "Edit" in the app And I press "Edit" in the app
And I set the following fields to these values in the app: And I set the following fields to these values in the app:
| URL | https://moodlecloud.com/ | | URL | https://moodlecloud.com/ |
@ -184,7 +184,7 @@ Feature: Users can manage entries in database activities
And I should not find "Moodle Cloud" in the app And I should not find "Moodle Cloud" in the app
# Edit the entry from single view. # Edit the entry from single view.
When I press "More" in the app When I press "Show more" in the app
And I should find "https://telegram.org/" in the app And I should find "https://telegram.org/" in the app
And I should find "Telegram" in the app And I should find "Telegram" in the app
And I press "Edit" in the app And I press "Edit" in the app

View File

@ -32,7 +32,7 @@ Feature: Test basic usage of comments in app
And I press "Add entries" in the app And I press "Add entries" in the app
And I set the field "Test field name" to "Test" in the app And I set the field "Test field name" to "Test" in the app
And I press "Save" in the app And I press "Save" in the app
And I press "More" in the app And I press "Show more" in the app
And I press "Comments (0)" in the app And I press "Comments (0)" in the app
And I set the field "Add a comment..." to "comment test teacher" in the app And I set the field "Add a comment..." to "comment test teacher" in the app
And I press "Send" in the app And I press "Send" in the app
@ -44,7 +44,7 @@ Feature: Test basic usage of comments in app
# Create and delete comments as a student # Create and delete comments as a student
Given I entered the data activity "Data" on course "Course 1" as "student1" in the app Given I entered the data activity "Data" on course "Course 1" as "student1" in the app
And I press "More" in the app And I press "Show more" in the app
And I press "Comments (1)" in the app And I press "Comments (1)" in the app
And I set the field "Add a comment..." to "comment test student" in the app And I set the field "Add a comment..." to "comment test student" in the app
And I press "Send" in the app And I press "Send" in the app
@ -69,7 +69,7 @@ Feature: Test basic usage of comments in app
And I press "Add entries" in the app And I press "Add entries" in the app
And I set the field "Test field name" to "Test" in the app And I set the field "Test field name" to "Test" in the app
And I press "Save" in the app And I press "Save" in the app
And I press "More" in the app And I press "Show more" in the app
And I press "Comments (0)" in the app And I press "Comments (0)" in the app
And I switch network connection to offline And I switch network connection to offline
And I set the field "Add a comment..." to "comment test" in the app And I set the field "Add a comment..." to "comment test" in the app