Merge pull request #1376 from dpalou/MOBILE-2431

Mobile 2431
main
Juan Leyva 2018-06-26 10:41:24 +02:00 committed by GitHub
commit 29388cdc47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 158 additions and 110 deletions

View File

@ -18,12 +18,12 @@
</ion-item-divider>
<ng-container *ngFor="let contact of contacts[contactType]">
<!-- Don't show deleted users -->
<ion-item text-wrap *ngIf="contact.profileimageurl || contact.profileimageurlsmall" [title]="contact.fullname" (click)="gotoDiscussion(contact.id)" [class.core-split-item-selected]="contact.id == discussionUserId" detail-none>
<a ion-item text-wrap *ngIf="contact.profileimageurl || contact.profileimageurlsmall" [title]="contact.fullname" (click)="gotoDiscussion(contact.id)" [class.core-split-item-selected]="contact.id == discussionUserId" detail-none>
<ion-avatar item-start>
<img src="{{contact.profileimageurl || contact.profileimageurlsmall}}" [alt]="'core.pictureof' | translate:{$a: contact.fullname}" core-external-content onError="this.src='assets/img/user-avatar.png'">
</ion-avatar>
<h2><core-format-text [text]="contact.fullname"></core-format-text></h2>
</ion-item>
</a>
</ng-container>
</ng-container>
</ion-list>

View File

@ -16,17 +16,17 @@
<h2>{{ 'core.searchresults' | translate }}</h2>
<ion-note item-end>{{ search.results.length }}</ion-note>
</ion-item-divider>
<ion-item text-wrap *ngFor="let result of search.results" [title]="result.fullname" (click)="gotoDiscussion(result.userid, result.messageid)" [class.core-split-item-selected]="result.userid == discussionUserId" detail-none>
<a ion-item text-wrap *ngFor="let result of search.results" [title]="result.fullname" (click)="gotoDiscussion(result.userid, result.messageid)" [class.core-split-item-selected]="result.userid == discussionUserId" detail-none>
<ion-avatar item-start>
<img src="{{result.profileimageurl}}" [alt]="'core.pictureof' | translate:{$a: result.fullname}" core-external-content onError="this.src='assets/img/user-avatar.png'">
</ion-avatar>
<h2><core-format-text [text]="result.fullname"></core-format-text></h2>
<p><core-format-text clean="true" singleLine="true" [text]="result.lastmessage"></core-format-text></p>
</ion-item>
</a>
</ion-list>
<ion-list *ngIf="!search.showResults" no-margin>
<ion-item text-wrap *ngFor="let discussion of discussions" [title]="discussion.fullname" (click)="gotoDiscussion(discussion.message.user)" [class.core-split-item-selected]="discussion.message.user == discussionUserId" detail-none>
<a ion-item text-wrap *ngFor="let discussion of discussions" [title]="discussion.fullname" (click)="gotoDiscussion(discussion.message.user)" [class.core-split-item-selected]="discussion.message.user == discussionUserId" detail-none>
<ion-avatar item-start>
<img src="{{discussion.profileimageurl}}" [alt]="'core.pictureof' | translate:{$a: discussion.fullname}" core-external-content onError="this.src='assets/img/user-avatar.png'">
</ion-avatar>
@ -38,7 +38,7 @@
</ion-note>
</h2>
<p><core-format-text clean="true" singleLine="true" [text]="discussion.message.message"></core-format-text></p>
</ion-item>
</a>
</ion-list>
</core-loading>
</ion-content>

View File

@ -201,14 +201,14 @@
</ion-item>
<!-- Data about the grader (teacher who graded). -->
<ion-item text-wrap *ngIf="grader" (click)="openUserProfile(grader.id)" [title]="grader.fullname" detail-push>
<a ion-item text-wrap *ngIf="grader" (click)="openUserProfile(grader.id)" [title]="grader.fullname" detail-push>
<ion-avatar item-start>
<img [src]="grader.profileimageurl" core-external-content [alt]="'core.pictureof' | translate:{$a: grader.fullname}" role="presentation" onError="this.src='assets/img/user-avatar.png'">
</ion-avatar>
<h2>{{ 'addon.mod_assign.gradedby' | translate }}</h2>
<h2>{{ grader.fullname }}</h2>
<p *ngIf="feedback.gradeddate">{{ feedback.gradeddate * 1000 | coreFormatDate:"dfmediumdate" }}</p>
</ion-item>
</a>
<!-- Warning message if cannot save grades. -->
<div *ngIf="isGrading && !canSaveGrades" class="core-warning-card" icon-start>

View File

@ -1,4 +1,4 @@
<ion-item text-wrap (click)="showComments()">
<a ion-item text-wrap (click)="showComments()" detail-none>
<h2>{{plugin.name}}</h2>
<core-comments contextLevel="module" [instanceId]="assign.cmid" component="assignsubmission_comments" [itemId]="submission.id" area="submission_comments" [title]="plugin.name"></core-comments>
</ion-item>
</a>

View File

@ -51,13 +51,13 @@
<ion-option *ngFor="let groupOpt of groupInfo.groups" [value]="groupOpt.id">{{groupOpt.name}}</ion-option>
</ion-select>
</ion-item>
<ion-item text-wrap *ngIf="access.canviewreports || access.canedititems" (click)="access.canviewreports && openFeature('Respondents')" [attr.detail-push]="access.canviewreports ? true : null">
<a ion-item text-wrap *ngIf="access.canviewreports || access.canedititems" (click)="access.canviewreports && openFeature('Respondents')" [attr.detail-none]="access.canviewreports ? null : true">
<h2>{{ 'addon.mod_feedback.completed_feedbacks' | translate }}</h2>
<ion-badge item-end>{{feedback.completedCount}}</ion-badge>
</ion-item>
<ion-item text-wrap *ngIf="!access.isanonymous && access.canviewreports" (click)="openFeature('NonRespondents')" detail-push>
</a>
<a ion-item text-wrap *ngIf="!access.isanonymous && access.canviewreports" (click)="openFeature('NonRespondents')" detail-push>
<h2>{{ 'addon.mod_feedback.show_nonrespondents' | translate }}</h2>
</ion-item>
</a>
<ion-item text-wrap *ngIf="access.canedititems">
<h2>{{ 'addon.mod_feedback.questions' | translate }}</h2>
<ion-badge item-end>{{feedback.itemsCount}}</ion-badge>

View File

@ -36,9 +36,9 @@
<ion-item-divider color="light">
{{ 'addon.mod_glossary.entriestobesynced' | translate }}
</ion-item-divider>
<ion-item *ngFor="let entry of offlineEntries" (click)="openNewEntry(entry)">
<a ion-item *ngFor="let entry of offlineEntries" (click)="openNewEntry(entry)" detail-none>
<p>{{entry.concept}}</p>
</ion-item>
</a>
</ion-list>
<ion-list *ngIf="entries.length > 0">
@ -48,9 +48,9 @@
{{getDivider(entry)}}
</ion-item-divider>
<ion-item (click)="openEntry(entry.id)" [class.core-split-item-selected]="entry.id == selectedEntry">
<a ion-item (click)="openEntry(entry.id)" [class.core-split-item-selected]="entry.id == selectedEntry" detail-none>
<p>{{entry.concept}}</p>
</ion-item>
</a>
</ng-container>
</ion-list>

View File

@ -1,5 +1,5 @@
<core-loading [hideUntil]="loaded">
<ion-item *ngIf="summary" text-wrap [attr.detail-push]="canViewAssessment && !canSelfAssess? true : null" (click)="gotoAssessment()">
<a ion-item *ngIf="summary" text-wrap [attr.detail-none]="canViewAssessment && !canSelfAssess? null : true" (click)="gotoAssessment()">
<ion-avatar item-start>
<img [src]="profile && profile.profileimageurl" core-external-content [alt]="'core.pictureof' | translate:{$a: profile && profile.fullname}" core-user-link [courseId]="courseId" [userId]="profile && profile.id" role="presentation" onError="this.src='assets/img/user-avatar.png'">
</ion-avatar>
@ -23,5 +23,5 @@
<ion-note item-end *ngIf="offline">
<ion-icon name="time"></ion-icon>{{ 'core.notsent' | translate }}
</ion-note>
</ion-item>
</a>
</core-loading>

View File

@ -15,11 +15,11 @@
<core-course-module-description *ngIf="description && selectedPhase == workshopPhases.PHASE_SETUP" [description]="description" [component]="component" [componentId]="componentId"></core-course-module-description>
<ion-card class="with-borders" *ngIf="phases">
<ion-item (click)="selectPhase()">
<a ion-item (click)="selectPhase()" detail-none>
<h2 stacked text-wrap>{{ phases[selectedPhase].title }}</h2>
<p text-wrap *ngIf="phases[selectedPhase].code == workshop.phase">{{ 'addon.mod_workshop.userplancurrentphase' | translate }}</p>
<ion-icon item-end name="arrow-dropdown"></ion-icon>
</ion-item>
</a>
<a ion-item text-wrap *ngIf="phases[selectedPhase].switchUrl" [href]="phases[selectedPhase].switchUrl" detail-none>
<ion-icon item-start name="swap"></ion-icon>
{{ 'addon.mod_workshop.switchphase' + selectedPhase | translate }}
@ -28,7 +28,7 @@
</ion-card>
<ion-card class="with-borders" *ngIf="phases && phases[selectedPhase] && phases[selectedPhase].tasks && phases[selectedPhase].tasks.length">
<ion-item text-wrap *ngFor="let task of phases[selectedPhase].tasks" [class.item-dimmed]="selectedPhase != workshop.phase" (click)="runTask(task)" detail-none>
<a ion-item text-wrap *ngFor="let task of phases[selectedPhase].tasks" [class.item-dimmed]="selectedPhase != workshop.phase" (click)="runTask(task)" detail-none>
<ion-icon item-start name="radio-button-off" *ngIf="task.completed == null"></ion-icon>
<ion-icon item-start name="close-circle" color="danger" *ngIf="task.completed == ''"></ion-icon>
<ion-icon item-start name="information-circle" color="info" *ngIf="task.completed == 'info'"></ion-icon>
@ -37,7 +37,7 @@
<h2>{{task.title}}</h2>
<p *ngIf="task.details"><core-format-text [text]="task.details"></core-format-text></p>
<ion-icon item-end *ngIf="task.link && !task.support" name="open"></ion-icon>
</ion-item>
</a>
</ion-card>
<!-- Has something offline. -->
@ -102,12 +102,12 @@
</ng-container>
<ion-card class="with-borders" *ngIf="!access.canviewallsubmissions && selectedPhase == workshop.phase && (canSubmit || canAssess) && selectedPhase == workshopPhases.PHASE_EVALUATION">
<ion-item text-wrap *ngIf="submission" (click)="switchPhase(workshopPhases.PHASE_SUBMISSION)" detail-push>
<a ion-item text-wrap *ngIf="submission" (click)="switchPhase(workshopPhases.PHASE_SUBMISSION)" detail-push>
<h2>{{ 'addon.mod_workshop.yoursubmission' | translate }}</h2>
</ion-item>
<ion-item text-wrap *ngIf="canAssess" (click)="switchPhase(workshopPhases.PHASE_ASSESSMENT)" detail-push>
</a>
<a ion-item text-wrap *ngIf="canAssess" (click)="switchPhase(workshopPhases.PHASE_ASSESSMENT)" detail-push>
<h2>{{ 'addon.mod_workshop.assignedassessments' | translate }}</h2>
</ion-item>
</a>
</ion-card>
<!-- CLOSED PHASE -->
@ -123,14 +123,14 @@
<ion-item-divider color="light" text-wrap>
<h2>{{ 'addon.mod_workshop.yourgrades' | translate }}</h2>
</ion-item-divider>
<ion-item text-wrap *ngIf="userGrades.submissionlongstrgrade" (click)="switchPhase(workshopPhases.PHASE_SUBMISSION)" detail-push>
<a ion-item text-wrap *ngIf="userGrades.submissionlongstrgrade" (click)="switchPhase(workshopPhases.PHASE_SUBMISSION)" detail-push>
<h2>{{ 'addon.mod_workshop.submissiongrade' | translate }}</h2>
<core-format-text [text]="userGrades.submissionlongstrgrade"></core-format-text>
</ion-item>
<ion-item text-wrap *ngIf="userGrades.assessmentlongstrgrade" (click)="switchPhase(workshopPhases.PHASE_ASSESSMENT)" detail-push>
</a>
<a ion-item text-wrap *ngIf="userGrades.assessmentlongstrgrade" (click)="switchPhase(workshopPhases.PHASE_ASSESSMENT)" detail-push>
<h2>{{ 'addon.mod_workshop.gradinggrade' | translate }}</h2>
<core-format-text [text]="userGrades.assessmentlongstrgrade"></core-format-text>
</ion-item>
</a>
</ion-card>
<ion-card class="with-borders" *ngIf="publishedSubmissions && publishedSubmissions.length">

View File

@ -47,7 +47,7 @@
</ion-item>
</div>
<ion-item text-wrap *ngIf="summary" [attr.detail-push]="submission.timemodified? true : null" (click)="gotoSubmission()">
<a ion-item text-wrap *ngIf="summary" [attr.detail-none]="submission.timemodified ? null : true" (click)="gotoSubmission()">
<ion-avatar item-start>
<img [src]="profile && profile.profileimageurl" core-external-content [alt]="'core.pictureof' | translate:{$a: profile && profile.fullname}" core-user-link [courseId]="courseId" [userId]="profile && profile.id" role="presentation" onError="this.src='assets/img/user-avatar.png'">
</ion-avatar>
@ -78,5 +78,5 @@
<div *ngIf="offline"><ion-icon name="time"></ion-icon> {{ 'core.notsent' | translate }}</div>
<div *ngIf="submission.deleted"><ion-icon name="trash"></ion-icon> {{ 'core.deletedoffline' | translate }}</div>
</ion-note>
</ion-item>
</a>
</core-loading>

View File

@ -376,7 +376,10 @@ export class CoreSite {
* @param {any} Config.
*/
setConfig(config: any): void {
config.tool_mobile_disabledfeatures = this.textUtils.treatDisabledFeatures(config.tool_mobile_disabledfeatures);
if (config) {
config.tool_mobile_disabledfeatures = this.textUtils.treatDisabledFeatures(config.tool_mobile_disabledfeatures);
}
this.config = config;
this.calculateOfflineDisabled();
}

View File

@ -1,3 +1,10 @@
<!-- Buttons to add to the header. *ngIf is needed, otherwise the component is executed too soon and doesn't find the header. -->
<core-navbar-buttons end *ngIf="loaded">
<core-context-menu>
<core-context-menu-item [hidden]="!displaySectionSelector || !sections || !sections.length" [priority]="500" [content]="'core.course.sections' | translate" (action)="showSectionSelector($event)" iconAction="menu"></core-context-menu-item>
</core-context-menu>
</core-navbar-buttons>
<!-- Default course format. -->
<core-dynamic-component [component]="courseFormatComponent" [data]="data">
<!-- Course summary. By default we only display the course progress. -->

View File

@ -10,10 +10,10 @@
</ion-header>
<ion-content>
<ng-container *ngFor="let section of sections">
<ion-item *ngIf="sectionHasContent(section)" text-wrap (click)="selectSection(section)" [class.core-primary-item]="selected.id == section.id" [class.item-dimmed]="section.visible === 0 || section.uservisible === false">
<a ion-item *ngIf="sectionHasContent(section)" text-wrap (click)="selectSection(section)" [class.core-primary-item]="selected.id == section.id" [class.item-dimmed]="section.visible === 0 || section.uservisible === false" detail-none>
<h2><core-format-text [text]="section.formattedName || section.name"></core-format-text></h2>
<ion-badge color="secondary" *ngIf="section.visible === 0">{{ 'core.course.nocontentavailable' | translate }}</ion-badge>
<ion-badge color="secondary" *ngIf="section.availabilityinfo"><core-format-text [text]=" section.availabilityinfo"></core-format-text></ion-badge>
</ion-item>
</a>
</ng-container>
</ion-content>

View File

@ -209,7 +209,7 @@ export class CoreCourseSectionPage implements OnDestroy {
// Get the overview files.
if (this.course.overviewfiles) {
this.course.imageThumb = this.course.overviewfiles[0] && this.course.overviewfiles[0].fileurl;
} else {
} else if (this.coursesProvider.isGetCoursesByFieldAvailable()) {
promises.push(this.coursesProvider.getCoursesByField('id', this.course.id).then((coursesInfo) => {
if (coursesInfo[0] && coursesInfo[0].overviewfiles && coursesInfo[0].overviewfiles[0]) {
this.course.imageThumb = coursesInfo[0].overviewfiles[0].fileurl;

View File

@ -97,6 +97,15 @@ export class CoreCourseFormatDefaultHandler implements CoreCourseFormatHandler {
* @return {any|Promise<any>} Current section (or promise resolved with current section).
*/
getCurrentSection(course: any, sections: any[]): any | Promise<any> {
if (!this.coursesProvider.isGetCoursesByFieldAvailable()) {
// Cannot get the current section, return the first one.
if (sections[0].id != CoreCourseProvider.ALL_SECTIONS_ID) {
return sections[0];
}
return sections[1];
}
// We need the "marker" to determine the current section.
return this.coursesProvider.getCoursesByField('id', course.id).catch(() => {
// Ignore errors.

View File

@ -1007,7 +1007,9 @@ export class CoreCourseHelperProvider {
});
// Prefetch other data needed to render the course.
promises.push(this.coursesProvider.getCoursesByField('id', course.id));
if (this.coursesProvider.isGetCoursesByFieldAvailable()) {
promises.push(this.coursesProvider.getCoursesByField('id', course.id));
}
promises.push(this.courseProvider.getActivitiesCompletionStatus(course.id));
return this.utils.allPromises(promises);

View File

@ -395,7 +395,9 @@ export class CoreCourseOptionsDelegate extends CoreDelegate {
* @return {Promise<void>} Promise resolved when done.
*/
protected loadCourseOptions(course: any, refresh?: boolean): Promise<void> {
if (typeof course.navOptions == 'undefined' || typeof course.admOptions == 'undefined' || refresh) {
if (this.coursesProvider.canGetAdminAndNavOptions() &&
(typeof course.navOptions == 'undefined' || typeof course.admOptions == 'undefined' || refresh)) {
return this.coursesProvider.getCoursesAdminAndNavOptions([course.id]).then((options) => {
course.navOptions = options.navOptions[course.id];
course.admOptions = options.admOptions[course.id];

View File

@ -96,7 +96,7 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
this.courseIds = courseIds.join(',');
if (this.courseIds) {
if (this.courseIds && this.coursesProvider.isGetCoursesByFieldAvailable()) {
// Load course image of all the courses.
promises.push(this.coursesProvider.getCoursesByField('ids', this.courseIds).then((coursesInfo) => {
coursesInfo = this.utils.arrayToObject(coursesInfo, 'id');
@ -111,12 +111,14 @@ export class CoreCoursesMyCoursesPage implements OnDestroy {
}));
}
promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
courses.forEach((course) => {
course.navOptions = options.navOptions[course.id];
course.admOptions = options.admOptions[course.id];
});
}));
if (this.coursesProvider.canGetAdminAndNavOptions()) {
promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
courses.forEach((course) => {
course.navOptions = options.navOptions[course.id];
course.admOptions = options.admOptions[course.id];
});
}));
}
return Promise.all(promises).then(() => {
this.courses = courses;

View File

@ -223,17 +223,19 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
return course.id;
});
// Load course options of the course.
promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
courses.forEach((course) => {
course.navOptions = options.navOptions[course.id];
course.admOptions = options.admOptions[course.id];
});
}));
if (this.coursesProvider.canGetAdminAndNavOptions()) {
// Load course options of the course.
promises.push(this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
courses.forEach((course) => {
course.navOptions = options.navOptions[course.id];
course.admOptions = options.admOptions[course.id];
});
}));
}
this.courseIds = courseIds.join(',');
if (this.courseIds) {
if (this.courseIds && this.coursesProvider.isGetCoursesByFieldAvailable()) {
// Load course image of all the courses.
promises.push(this.coursesProvider.getCoursesByField('ids', this.courseIds).then((coursesInfo) => {
coursesInfo = this.utils.arrayToObject(coursesInfo, 'id');

View File

@ -33,6 +33,16 @@ export class CoreCoursesProvider {
this.logger = logger.getInstance('CoreCoursesProvider');
}
/**
* Whether current site supports getting course options.
*
* @return {boolean} Whether current site supports getting course options.
*/
canGetAdminAndNavOptions(): boolean {
return this.sitesProvider.wsAvailableInCurrentSite('core_course_get_user_navigation_options') &&
this.sitesProvider.wsAvailableInCurrentSite('core_course_get_user_administration_options');
}
/**
* Get categories. They can be filtered by id.
*

View File

@ -11,7 +11,7 @@
</ion-header>
<ion-content class="has-fab">
<ion-list>
<ion-item (click)="login(site.id)" *ngFor="let site of sites; let idx = index">
<a ion-item (click)="login(site.id)" *ngFor="let site of sites; let idx = index" detail-none>
<ion-avatar item-start>
<img [src]="site.avatar" core-external-content [siteId]="site.id" alt="{{ 'core.pictureof' | translate:{$a: site.fullname} }}" role="presentation" onError="this.src='assets/img/user-avatar.png'">
</ion-avatar>
@ -22,7 +22,7 @@
<button *ngIf="showDelete" item-end ion-button icon-only clear color="danger" (click)="deleteSite($event, idx)" [attr.aria-label]="'core.delete' | translate">
<ion-icon name="trash"></ion-icon>
</button>
</ion-item>
</a>
</ion-list>
<ion-fab bottom right>
<button ion-fab (click)="add()" [attr.aria-label]="'core.add' | translate">

View File

@ -15,12 +15,12 @@
<ion-item text-center *ngIf="(!handlers || !handlers.length) && !handlersLoaded">
<ion-spinner></ion-spinner>
</ion-item>
<ion-item *ngFor="let handler of handlers" [ngClass]="['core-moremenu-handler', handler.class]" (click)="openHandler(handler)" title="{{ handler.title | translate }}" detail-push>
<a ion-item *ngFor="let handler of handlers" [ngClass]="['core-moremenu-handler', handler.class]" (click)="openHandler(handler)" title="{{ handler.title | translate }}" detail-push>
<core-icon [name]="handler.icon" item-start></core-icon>
<p>{{ handler.title | translate}}</p>
<ion-badge item-end *ngIf="handler.showBadge" [hidden]="handler.loading || !handler.badge">{{handler.badge}}</ion-badge>
<ion-spinner item-end *ngIf="handler.showBadge && handler.loading"></ion-spinner>
</ion-item>
</a>
<div *ngFor="let item of customItems" class="core-moremenu-customitem">
<a ion-item *ngIf="item.type != 'embedded'" [href]="item.url" core-link [capture]="item.type == 'app'" [inApp]="item.type == 'inappbrowser'" title="{{item.label}}">
<core-icon [name]="item.icon" item-start></core-icon>

View File

@ -8,32 +8,32 @@
<core-split-view>
<ion-content>
<ion-list>
<ion-item (click)="openHandler('CoreSettingsGeneralPage')" [title]="'core.settings.general' | translate" [class.core-split-item-selected]="'CoreSettingsGeneralPage' == selectedPage" detail-push>
<a ion-item (click)="openHandler('CoreSettingsGeneralPage')" [title]="'core.settings.general' | translate" [class.core-split-item-selected]="'CoreSettingsGeneralPage' == selectedPage" detail-push>
<ion-icon name="construct" item-start></ion-icon>
<p>{{ 'core.settings.general' | translate }}</p>
</ion-item>
<ion-item (click)="openHandler('CoreSettingsSpaceUsagePage')" [title]="'core.settings.spaceusage' | translate" [class.core-split-item-selected]="'CoreSettingsSpaceUsagePage' == selectedPage" detail-push>
</a>
<a ion-item (click)="openHandler('CoreSettingsSpaceUsagePage')" [title]="'core.settings.spaceusage' | translate" [class.core-split-item-selected]="'CoreSettingsSpaceUsagePage' == selectedPage" detail-push>
<ion-icon name="stats" item-start></ion-icon>
<p>{{ 'core.settings.spaceusage' | translate }}</p>
</ion-item>
<ion-item (click)="openHandler('CoreSettingsSynchronizationPage')" [title]="'core.settings.synchronization' | translate" [class.core-split-item-selected]="'CoreSettingsSynchronizationPage' == selectedPage" detail-push>
</a>
<a ion-item (click)="openHandler('CoreSettingsSynchronizationPage')" [title]="'core.settings.synchronization' | translate" [class.core-split-item-selected]="'CoreSettingsSynchronizationPage' == selectedPage" detail-push>
<ion-icon name="sync" item-start></ion-icon>
<p>{{ 'core.settings.synchronization' | translate }}</p>
</ion-item>
<ion-item *ngIf="isIOS" (click)="openHandler('CoreSharedFilesListPage', {manage: true})" [title]="'core.sharedfiles.sharedfiles' | translate" [class.core-split-item-selected]="'CoreSharedFilesListPage' == selectedPage" detail-push>
</a>
<a ion-item *ngIf="isIOS" (click)="openHandler('CoreSharedFilesListPage', {manage: true})" [title]="'core.sharedfiles.sharedfiles' | translate" [class.core-split-item-selected]="'CoreSharedFilesListPage' == selectedPage" detail-push>
<ion-icon name="folder" item-start></ion-icon>
<p>{{ 'core.sharedfiles.sharedfiles' | translate }}</p>
</ion-item>
</a>
<ion-item *ngFor="let handler of handlers" [ngClass]="['core-settings-handler', handler.class]" (click)="openHandler(handler.page, handler.params)" [title]="handler.title | translate" detail-push [class.core-split-item-selected]="handler.page == selectedPage">
<a ion-item *ngFor="let handler of handlers" [ngClass]="['core-settings-handler', handler.class]" (click)="openHandler(handler.page, handler.params)" [title]="handler.title | translate" detail-push [class.core-split-item-selected]="handler.page == selectedPage">
<core-icon [name]="handler.icon" item-start *ngIf="handler.icon"></core-icon>
<p>{{ handler.title | translate}}</p>
</ion-item>
</a>
<ion-item (click)="openHandler('CoreSettingsAboutPage')" [title]="'core.settings.about' | translate" [class.core-split-item-selected]="'CoreSettingsAboutPage' == selectedPage" detail-push>
<a ion-item (click)="openHandler('CoreSettingsAboutPage')" [title]="'core.settings.about' | translate" [class.core-split-item-selected]="'CoreSettingsAboutPage' == selectedPage" detail-push>
<ion-icon name="contacts" item-start></ion-icon>
<p>{{ 'core.settings.about' | translate }}</p>
</ion-item>
</a>
</ion-list>
</ion-content>
</core-split-view>

View File

@ -213,51 +213,62 @@ export class CoreUserDelegate extends CoreDelegate {
* @return {Subject<CoreUserProfileHandlerToDisplay[]>} Resolved with the handlers.
*/
getProfileHandlersFor(user: any, courseId: number): Subject<CoreUserProfileHandlerToDisplay[]> {
const promises = [];
let promise,
navOptions,
admOptions;
if (this.coursesProvider.canGetAdminAndNavOptions()) {
// Get course options.
promise = this.coursesProvider.getUserCourses(true).then((courses) => {
const courseIds = courses.map((course) => {
return course.id;
});
return this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
// For backwards compatibility we don't modify the courseId.
const courseIdForOptions = courseId || this.sitesProvider.getCurrentSiteHomeId();
navOptions = options.navOptions[courseIdForOptions];
admOptions = options.admOptions[courseIdForOptions];
});
});
} else {
promise = Promise.resolve();
}
this.userHandlers = [];
// Retrieve course options forcing cache.
this.coursesProvider.getUserCourses(true).then((courses) => {
const courseIds = courses.map((course) => {
return course.id;
});
promise.then(() => {
const promises = [];
return this.coursesProvider.getCoursesAdminAndNavOptions(courseIds).then((options) => {
// For backwards compatibility we don't modify the courseId.
const courseIdForOptions = courseId || this.sitesProvider.getCurrentSiteHomeId(),
navOptions = options.navOptions[courseIdForOptions],
admOptions = options.admOptions[courseIdForOptions];
for (const name in this.enabledHandlers) {
// Checks if the handler is enabled for the user.
const handler = <CoreUserProfileHandler> this.handlers[name],
isEnabledForUser = handler.isEnabledForUser(user, courseId, navOptions, admOptions),
promise = Promise.resolve(isEnabledForUser).then((enabled) => {
if (enabled) {
this.userHandlers.push({
name: name,
data: handler.getDisplayData(user, courseId),
priority: handler.priority,
type: handler.type || CoreUserDelegate.TYPE_NEW_PAGE
});
} else {
return Promise.reject(null);
}
}).catch(() => {
// Nothing to do here, it is not enabled for this user.
});
promises.push(promise);
}
return Promise.all(promises).then(() => {
// Sort them by priority.
this.userHandlers.sort((a, b) => {
return b.priority - a.priority;
for (const name in this.enabledHandlers) {
// Checks if the handler is enabled for the user.
const handler = <CoreUserProfileHandler> this.handlers[name],
isEnabledForUser = handler.isEnabledForUser(user, courseId, navOptions, admOptions),
promise = Promise.resolve(isEnabledForUser).then((enabled) => {
if (enabled) {
this.userHandlers.push({
name: name,
data: handler.getDisplayData(user, courseId),
priority: handler.priority,
type: handler.type || CoreUserDelegate.TYPE_NEW_PAGE
});
} else {
return Promise.reject(null);
}
}).catch(() => {
// Nothing to do here, it is not enabled for this user.
});
this.loaded = true;
this.observableHandlers.next(this.userHandlers);
promises.push(promise);
}
return Promise.all(promises).then(() => {
// Sort them by priority.
this.userHandlers.sort((a, b) => {
return b.priority - a.priority;
});
this.loaded = true;
this.observableHandlers.next(this.userHandlers);
});
}).catch(() => {
// Never fails.