MOBILE-4543 course: Do not show open in browser for students
parent
4f56e08f9b
commit
9038e883e7
|
@ -79,14 +79,11 @@ Feature: Test basic usage of BBB activity in app
|
|||
And I should not be able to press "Join session" in the app
|
||||
|
||||
# Join the session as moderator in a browser.
|
||||
When I press "Information" in the app
|
||||
And I press "Open in browser" in the app
|
||||
And I switch to the browser tab opened by the app
|
||||
And I log in as "teacher1"
|
||||
When I open a browser tab with url "$WWWROOT"
|
||||
And I am on the "bbb1" Activity page logged in as teacher1
|
||||
And I click on "Join session" "link"
|
||||
And I wait for the BigBlueButton room to start
|
||||
And I switch back to the app
|
||||
And I press "Close" in the app
|
||||
And I pull to refresh until I find "The session is in progress" in the app
|
||||
Then I should find "1" near "Moderator" in the app
|
||||
And I should find "0" near "Viewer" in the app
|
||||
|
|
|
@ -31,10 +31,8 @@ Feature: Test basic usage of choice activity in app
|
|||
Given I entered the choice activity "Choice name" on course "Course 1" as "teacher1" in the app
|
||||
Then I should find "Test choice description" in the app
|
||||
|
||||
When I press "Information" in the app
|
||||
And I press "Open in browser" in the app
|
||||
And I switch to the browser tab opened by the app
|
||||
And I log in as "teacher1"
|
||||
When I open a browser tab with url "$WWWROOT"
|
||||
And I am on the "choice1" Activity page logged in as teacher1
|
||||
And I press "Actions menu"
|
||||
And I follow "View 1 responses"
|
||||
And I press "Download in text format"
|
||||
|
|
|
@ -176,10 +176,8 @@ Feature: Test basic usage of choice activity in app
|
|||
Given I entered the choice activity "Choice name" on course "Course 1" as "teacher1" in the app
|
||||
Then I should find "Test choice description" in the app
|
||||
|
||||
When I press "Information" in the app
|
||||
And I press "Open in browser" in the app
|
||||
And I switch to the browser tab opened by the app
|
||||
And I log in as "teacher1"
|
||||
When I open a browser tab with url "$WWWROOT"
|
||||
And I am on the "choice1" Activity page logged in as teacher1
|
||||
And I follow "Responses"
|
||||
And I press "Download in text format"
|
||||
# TODO Then I should find "..." in the downloads folder
|
||||
|
|
|
@ -207,12 +207,9 @@ Feature: Attempt a quiz in app
|
|||
And I replace "/.*/" within "page-addon-mod-quiz-review core-loading > ion-card ion-item:nth-child(3) p:nth-child(2)" with "[Completed on date]"
|
||||
Then the UI should match the snapshot
|
||||
|
||||
Given I entered the quiz activity "Quiz 1" on course "Course 1" as "teacher1" in the app
|
||||
When I press "Information" in the app
|
||||
And I press "Open in browser" in the app
|
||||
And I switch to the browser tab opened by the app
|
||||
And I log in as "teacher1"
|
||||
And I follow "Attempts: 1"
|
||||
Given I open a browser tab with url "$WWWROOT"
|
||||
And I am on the "quiz1" Activity page logged in as teacher1
|
||||
When I follow "Attempts: 1"
|
||||
And I follow "Review attempt"
|
||||
Then I should see "Finished"
|
||||
And I should see "1.00/2.00"
|
||||
|
|
|
@ -206,11 +206,8 @@ Feature: Attempt a quiz in app
|
|||
When I replace "/.*/" within "page-addon-mod-quiz-review core-loading > ion-card ion-item:nth-child(1) p:nth-child(2)" with "[Started on date]"
|
||||
And I replace "/.*/" within "page-addon-mod-quiz-review core-loading > ion-card ion-item:nth-child(3) p:nth-child(2)" with "[Completed on date]"
|
||||
|
||||
Given I entered the quiz activity "Quiz 1" on course "Course 1" as "teacher1" in the app
|
||||
When I press "Information" in the app
|
||||
And I press "Open in browser" in the app
|
||||
And I switch to the browser tab opened by the app
|
||||
And I log in as "teacher1"
|
||||
Given I open a browser tab with url "$WWWROOT"
|
||||
When I am on the "quiz1" Activity page logged in as teacher1
|
||||
And I follow "Attempts: 1"
|
||||
And I follow "Review attempt"
|
||||
Then I should see "Finished"
|
||||
|
|
|
@ -211,12 +211,9 @@ Feature: Attempt a quiz in app
|
|||
And I replace "/.*/" within "page-addon-mod-quiz-review core-loading > ion-card ion-item:nth-child(3) p:nth-child(2)" with "[Completed on date]"
|
||||
Then the UI should match the snapshot
|
||||
|
||||
Given I entered the quiz activity "Quiz 1" on course "Course 1" as "teacher1" in the app
|
||||
When I press "Information" in the app
|
||||
And I press "Open in browser" in the app
|
||||
And I switch to the browser tab opened by the app
|
||||
And I log in as "teacher1"
|
||||
And I follow "Attempts: 1"
|
||||
Given I open a browser tab with url "$WWWROOT"
|
||||
And I am on the "quiz1" Activity page logged in as teacher1
|
||||
When I follow "Attempts: 1"
|
||||
And I follow "Review attempt"
|
||||
Then I should see "Finished"
|
||||
And I should see "1.00/2.00"
|
||||
|
|
|
@ -233,8 +233,7 @@ Feature: Test basic usage of SCORM activity in app
|
|||
Then I should find "2 / 11" in the app
|
||||
|
||||
When I open a browser tab with url "$WWWROOT"
|
||||
And I log in as "admin"
|
||||
And I am on the "System logs report" page
|
||||
And I am on the "System logs report" page logged in as "admin"
|
||||
And I set the field "id" to "Course 1"
|
||||
And I set the field "user" to "Student student"
|
||||
And I press "Get these logs"
|
||||
|
|
|
@ -24,7 +24,8 @@
|
|||
</h1>
|
||||
</ion-label>
|
||||
<ion-button fill="clear" *ngIf="displayOptions.displayOpenInBrowser && externalUrl" [href]="externalUrl" core-link
|
||||
[showBrowserWarning]="false" [attr.aria-label]="'core.openinbrowser' | translate" slot="end">
|
||||
[showBrowserWarning]="false" [attr.aria-label]="'core.openinbrowser' | translate" slot="end" [class.hidden]="!isTeacher"
|
||||
class="core-module-oib-button">
|
||||
<ion-icon name="fas-up-right-from-square" slot="icon-only" aria-hidden="true" />
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
|
|
|
@ -39,3 +39,7 @@ ion-item.card-header {
|
|||
margin: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
.core-module-oib-button.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -70,6 +70,7 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy {
|
|||
course?: CoreEnrolledCourseData;
|
||||
modicon = '';
|
||||
moduleNameTranslated = '';
|
||||
isTeacher = false;
|
||||
|
||||
protected onlineSubscription: Subscription; // It will observe the status of the network connection.
|
||||
protected packageStatusObserver?: CoreEventObserver; // Observer of package status.
|
||||
|
@ -269,13 +270,14 @@ export class CoreCourseModuleSummaryComponent implements OnInit, OnDestroy {
|
|||
* Fetch course.
|
||||
*/
|
||||
protected async fetchCourse(): Promise<void> {
|
||||
// Fix that.
|
||||
try {
|
||||
this.course = await CoreCourses.getUserCourse(this.courseId, true);
|
||||
} catch {
|
||||
// The user is not enrolled in the course. Use getCourses to see if it's an admin/manager and can see the course.
|
||||
this.course = await CoreCourses.getCourse(this.courseId);
|
||||
}
|
||||
|
||||
this.isTeacher = await CoreUtils.ignoreErrors(CoreCourseHelper.guessIsTeacher(this.courseId, this.course), false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -41,7 +41,8 @@
|
|||
</ion-chip>
|
||||
</ion-label>
|
||||
<ion-button *ngIf="displayOpenInBrowser" fill="clear" [href]="courseUrl" core-link [showBrowserWarning]="false"
|
||||
[attr.aria-label]="'core.openinbrowser' | translate" slot="end">
|
||||
[attr.aria-label]="'core.openinbrowser' | translate" slot="end" [class.hidden]="!isTeacher"
|
||||
class="core-course-oib-button">
|
||||
<ion-icon name="fas-up-right-from-square" slot="icon-only" aria-hidden="true" />
|
||||
</ion-button>
|
||||
</ion-item>
|
||||
|
|
|
@ -73,6 +73,7 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
progress?: number;
|
||||
courseMenuHandlers: CoreCourseOptionsMenuHandlerToDisplay[] = [];
|
||||
displayOpenInBrowser = false;
|
||||
isTeacher = false;
|
||||
|
||||
protected actionSheet?: HTMLIonActionSheetElement;
|
||||
protected waitStart = 0;
|
||||
|
@ -172,6 +173,9 @@ export class CoreCourseSummaryPage implements OnInit, OnDestroy {
|
|||
|
||||
await this.loadMenuHandlers(refresh);
|
||||
|
||||
// After loading menu handlers, admOptions should be available.
|
||||
this.isTeacher = await CoreUtils.ignoreErrors(CoreCourseHelper.guessIsTeacher(this.courseId, this.course), false);
|
||||
|
||||
this.dataLoaded = true;
|
||||
}
|
||||
|
||||
|
|
|
@ -104,4 +104,8 @@
|
|||
display: inline;
|
||||
}
|
||||
}
|
||||
|
||||
.core-course-oib-button.hidden {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2058,6 +2058,30 @@ export class CoreCourseHelperProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Guess if the user is a teacher in a course.
|
||||
*
|
||||
* @param courseId Course Id.
|
||||
* @param course Course object.
|
||||
* @returns Promise resolved with boolean: whether the user is a teacher.
|
||||
*/
|
||||
async guessIsTeacher(
|
||||
courseId: number,
|
||||
course?: CoreEnrolledCourseData | CoreCourseSearchedData,
|
||||
): Promise<boolean> {
|
||||
if (course && 'admOptions' in course && course.admOptions) {
|
||||
return !!course.admOptions['reports'];
|
||||
}
|
||||
|
||||
// Not loaded yet, try to load it.
|
||||
const adminOptions = await CoreCourses.getUserAdministrationOptions(
|
||||
[courseId],
|
||||
{ readingStrategy: CoreSitesReadingStrategy.PREFER_CACHE },
|
||||
);
|
||||
|
||||
return !!adminOptions[courseId]?.['reports'];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreCourseHelper = makeSingleton(CoreCourseHelperProvider);
|
||||
|
|
|
@ -101,18 +101,9 @@ Feature: Test basic usage of one course in app
|
|||
And I should not find "Test glossary" in the app
|
||||
|
||||
Scenario: Guest access
|
||||
Given I entered the course "Course 1" as "teacher1" in the app
|
||||
And I press "Course summary" in the app
|
||||
And I press "Open in browser" in the app
|
||||
And I switch to the browser tab opened by the app
|
||||
And I log in as "teacher1"
|
||||
And I press "Actions menu"
|
||||
And I follow "More..."
|
||||
And I follow "Users"
|
||||
And I follow "Enrolment methods"
|
||||
Given I am on the "Course 1" "enrolment methods" page logged in as "teacher1"
|
||||
And I click on "Enable" "icon" in the "Guest access" "table_row"
|
||||
And I close the browser tab opened by the app
|
||||
Given I entered the app as "student2"
|
||||
And I entered the app as "student2"
|
||||
When I press "Site home" in the app
|
||||
And I press "Available courses" in the app
|
||||
And I press "Course 1" in the app
|
||||
|
|
|
@ -23,8 +23,7 @@ Feature: Test basic usage of guest access course in app
|
|||
|
||||
@lms_from4.0
|
||||
Scenario: Guest access without password (student)
|
||||
Given I log in as "teacher1"
|
||||
And I am on the "Course 1" "enrolment methods" page
|
||||
Given I am on the "Course 1" "enrolment methods" page logged in as "teacher1"
|
||||
And I click on "Edit" "link" in the "Guest access" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Allow guest access | Yes |
|
||||
|
@ -47,8 +46,7 @@ Feature: Test basic usage of guest access course in app
|
|||
|
||||
@lms_from4.3
|
||||
Scenario: Guest access with password (student)
|
||||
Given I log in as "teacher1"
|
||||
And I am on the "Course 1" "enrolment methods" page
|
||||
Given I am on the "Course 1" "enrolment methods" page logged in as "teacher1"
|
||||
And I click on "Edit" "link" in the "Guest access" "table_row"
|
||||
And I set the following fields to these values:
|
||||
| Allow guest access | Yes |
|
||||
|
|
|
@ -27,8 +27,6 @@ import { AddonEnrolSelf } from '@addons/enrol/self/services/self';
|
|||
import { CoreEnrol, CoreEnrolEnrolmentInfo, CoreEnrolEnrolmentMethod } from '@features/enrol/services/enrol';
|
||||
import { CoreSiteWSPreSets, WSObservable } from '@classes/sites/authenticated-site';
|
||||
|
||||
const ROOT_CACHE_KEY = 'mmCourses:';
|
||||
|
||||
declare module '@singletons/events' {
|
||||
|
||||
/**
|
||||
|
@ -50,6 +48,8 @@ declare module '@singletons/events' {
|
|||
@Injectable({ providedIn: 'root' })
|
||||
export class CoreCoursesProvider {
|
||||
|
||||
protected static readonly ROOT_CACHE_KEY = 'mmCourses:';
|
||||
|
||||
static readonly SEARCH_PER_PAGE = 20;
|
||||
static readonly RECENT_PER_PAGE = 10;
|
||||
static readonly ENROL_INVALID_KEY = 'CoreCoursesEnrolInvalidKey';
|
||||
|
@ -114,7 +114,7 @@ export class CoreCoursesProvider {
|
|||
* @returns Cache key.
|
||||
*/
|
||||
protected getCategoriesCacheKey(categoryId: number, addSubcategories?: boolean): string {
|
||||
return ROOT_CACHE_KEY + 'categories:' + categoryId + ':' + !!addSubcategories;
|
||||
return `${CoreCoursesProvider.ROOT_CACHE_KEY}categories:${categoryId}:${!!addSubcategories}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -131,16 +131,16 @@ export class CoreCoursesProvider {
|
|||
if (courseIds.length == 1) {
|
||||
// Only 1 course, check if it belongs to the user courses. If so, use all user courses.
|
||||
return this.getCourseIdsIfEnrolled(courseIds[0], siteId);
|
||||
} else {
|
||||
if (courseIds.length > 1 && courseIds.indexOf(siteHomeId) == -1) {
|
||||
courseIds.push(siteHomeId);
|
||||
}
|
||||
|
||||
// Sort the course IDs.
|
||||
courseIds.sort((a, b) => b - a);
|
||||
|
||||
return courseIds;
|
||||
}
|
||||
|
||||
if (courseIds.length > 1 && courseIds.indexOf(siteHomeId) == -1) {
|
||||
courseIds.push(siteHomeId);
|
||||
}
|
||||
|
||||
// Sort the course IDs.
|
||||
courseIds.sort((a, b) => b - a);
|
||||
|
||||
return courseIds;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -363,7 +363,7 @@ export class CoreCoursesProvider {
|
|||
* @returns Cache key.
|
||||
*/
|
||||
protected getCoursesCacheKey(ids: number[]): string {
|
||||
return ROOT_CACHE_KEY + 'course:' + JSON.stringify(ids);
|
||||
return `${CoreCoursesProvider.ROOT_CACHE_KEY}course:${JSON.stringify(ids)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -536,7 +536,7 @@ export class CoreCoursesProvider {
|
|||
* @returns Cache key.
|
||||
*/
|
||||
protected getCoursesByFieldCacheKey(field: string = '', value: string | number = ''): string {
|
||||
return ROOT_CACHE_KEY + 'coursesbyfield:' + field + ':' + value;
|
||||
return `${CoreCoursesProvider.ROOT_CACHE_KEY}coursesbyfield:${field}:${value}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -651,7 +651,7 @@ export class CoreCoursesProvider {
|
|||
* @returns Cache key.
|
||||
*/
|
||||
protected getRecentCoursesCacheKey(userId: number): string {
|
||||
return `${ROOT_CACHE_KEY}:recentcourses:${userId}`;
|
||||
return `${CoreCoursesProvider.ROOT_CACHE_KEY}:recentcourses:${userId}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -684,7 +684,7 @@ export class CoreCoursesProvider {
|
|||
* @returns Cache key.
|
||||
*/
|
||||
protected getUserAdministrationOptionsCommonCacheKey(): string {
|
||||
return ROOT_CACHE_KEY + 'administrationOptions:';
|
||||
return `${CoreCoursesProvider.ROOT_CACHE_KEY}administrationOptions:`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -701,11 +701,14 @@ export class CoreCoursesProvider {
|
|||
* Get user administration options for a set of courses.
|
||||
*
|
||||
* @param courseIds IDs of courses to get.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @param options Options.
|
||||
* @returns Promise resolved with administration options for each course.
|
||||
*/
|
||||
getUserAdministrationOptions(courseIds: number[], siteId?: string): Promise<CoreCourseUserAdminOrNavOptionCourseIndexed> {
|
||||
return firstValueFrom(this.getUserAdministrationOptionsObservable(courseIds, { siteId }));
|
||||
getUserAdministrationOptions(
|
||||
courseIds: number[],
|
||||
options?: CoreSitesCommonWSOptions,
|
||||
): Promise<CoreCourseUserAdminOrNavOptionCourseIndexed> {
|
||||
return firstValueFrom(this.getUserAdministrationOptionsObservable(courseIds, options));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -752,7 +755,7 @@ export class CoreCoursesProvider {
|
|||
* @returns Cache key.
|
||||
*/
|
||||
protected getUserNavigationOptionsCommonCacheKey(): string {
|
||||
return ROOT_CACHE_KEY + 'navigationOptions:';
|
||||
return `${CoreCoursesProvider.ROOT_CACHE_KEY}navigationOptions:`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -768,11 +771,14 @@ export class CoreCoursesProvider {
|
|||
* Get user navigation options for a set of courses.
|
||||
*
|
||||
* @param courseIds IDs of courses to get.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @param options Options.
|
||||
* @returns Promise resolved with navigation options for each course.
|
||||
*/
|
||||
async getUserNavigationOptions(courseIds: number[], siteId?: string): Promise<CoreCourseUserAdminOrNavOptionCourseIndexed> {
|
||||
return firstValueFrom(this.getUserNavigationOptionsObservable(courseIds, { siteId }));
|
||||
getUserNavigationOptions(
|
||||
courseIds: number[],
|
||||
options?: CoreSitesCommonWSOptions,
|
||||
): Promise<CoreCourseUserAdminOrNavOptionCourseIndexed> {
|
||||
return firstValueFrom(this.getUserNavigationOptionsObservable(courseIds, options));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -981,7 +987,7 @@ export class CoreCoursesProvider {
|
|||
* @returns Cache key.
|
||||
*/
|
||||
protected getUserCoursesCacheKey(): string {
|
||||
return ROOT_CACHE_KEY + 'usercourses';
|
||||
return `${CoreCoursesProvider.ROOT_CACHE_KEY}usercourses`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,22 +4,22 @@ Feature: It opens external links properly.
|
|||
Background:
|
||||
Given the following "users" exist:
|
||||
| username |
|
||||
| student1 |
|
||||
| teacher1 |
|
||||
And the following "courses" exist:
|
||||
| fullname | shortname |
|
||||
| Course 1 | C1 |
|
||||
And the following "course enrolments" exist:
|
||||
| user | course | role |
|
||||
| student1 | C1 | student |
|
||||
| teacher1 | C1 | teacher |
|
||||
And the following "activities" exist:
|
||||
| activity | name | intro | course | idnumber |
|
||||
| forum | Test forum | Test forum | C1 | forum |
|
||||
And the following forum discussions exist in course "Course 1":
|
||||
| forum | user | name | message |
|
||||
| Test forum | student1 | Forum topic | See <a href="https://moodle.org/">moodle.org external link</a> |
|
||||
| Test forum | teacher1 | Forum topic | See <a href="https://moodle.org/">moodle.org external link</a> |
|
||||
|
||||
Scenario: Click an external link
|
||||
Given I entered the forum activity "Test forum" on course "Course 1" as "student1" in the app
|
||||
Given I entered the forum activity "Test forum" on course "Course 1" as "teacher1" in the app
|
||||
When I press "Forum topic" in the app
|
||||
And I press "moodle.org external link" in the app
|
||||
Then I should find "You are about to leave the app" in the app
|
||||
|
|
Loading…
Reference in New Issue