Merge pull request #2185 from crazyserver/MOBILE-3210
MOBILE-3210 myoverview: Add custom filtersmain
commit
09a033acd5
|
@ -1,8 +1,8 @@
|
||||||
<ion-item-divider>
|
<ion-item-divider>
|
||||||
<h2>{{ 'addon.block_myoverview.pluginname' | translate }}</h2>
|
<h2>{{ 'addon.block_myoverview.pluginname' | translate }}</h2>
|
||||||
<!-- Download all courses. -->
|
<!-- Download all courses. -->
|
||||||
<div *ngIf="downloadCoursesEnabled && downloadEnabled && courses[selectedFilter] && courses[selectedFilter].length > 1 && !showFilter" class="core-button-spinner" item-end>
|
<div *ngIf="downloadCoursesEnabled && downloadEnabled && filteredCourses.length > 1 && !showFilter" class="core-button-spinner" item-end>
|
||||||
<button *ngIf="prefetchCoursesData[selectedFilter].icon && prefetchCoursesData[selectedFilter].icon != 'spinner'" ion-button icon-only clear color="dark" (click)="prefetchCourses()">
|
<button *ngIf="prefetchCoursesData[selectedFilter] && prefetchCoursesData[selectedFilter].icon && prefetchCoursesData[selectedFilter].icon != 'spinner'" ion-button icon-only clear color="dark" (click)="prefetchCourses()">
|
||||||
<core-icon [name]="prefetchCoursesData[selectedFilter].icon"></core-icon>
|
<core-icon [name]="prefetchCoursesData[selectedFilter].icon"></core-icon>
|
||||||
</button>
|
</button>
|
||||||
<ion-badge class="core-course-download-courses-progress" *ngIf="prefetchCoursesData[selectedFilter].badge">{{prefetchCoursesData[selectedFilter].badge}}</ion-badge>
|
<ion-badge class="core-course-download-courses-progress" *ngIf="prefetchCoursesData[selectedFilter].badge">{{prefetchCoursesData[selectedFilter].badge}}</ion-badge>
|
||||||
|
@ -23,11 +23,16 @@
|
||||||
<ion-option value="inprogress" *ngIf="showFilters.inprogress != 'hidden'" [disabled]="showFilters.inprogress == 'disabled'">{{ 'addon.block_myoverview.inprogress' | translate }}</ion-option>
|
<ion-option value="inprogress" *ngIf="showFilters.inprogress != 'hidden'" [disabled]="showFilters.inprogress == 'disabled'">{{ 'addon.block_myoverview.inprogress' | translate }}</ion-option>
|
||||||
<ion-option value="future" *ngIf="showFilters.future != 'hidden'" [disabled]="showFilters.future == 'disabled'">{{ 'addon.block_myoverview.future' | translate }}</ion-option>
|
<ion-option value="future" *ngIf="showFilters.future != 'hidden'" [disabled]="showFilters.future == 'disabled'">{{ 'addon.block_myoverview.future' | translate }}</ion-option>
|
||||||
<ion-option value="past" *ngIf="showFilters.past != 'hidden'" [disabled]="showFilters.past == 'disabled'">{{ 'addon.block_myoverview.past' | translate }}</ion-option>
|
<ion-option value="past" *ngIf="showFilters.past != 'hidden'" [disabled]="showFilters.past == 'disabled'">{{ 'addon.block_myoverview.past' | translate }}</ion-option>
|
||||||
|
<ng-container *ngIf="showFilters.custom != 'hidden'">
|
||||||
|
<ng-container *ngFor="let customOption of customFilter; let index = index">
|
||||||
|
<ion-option value="custom-{{index}}">{{ customOption.name }}</ion-option>
|
||||||
|
</ng-container>
|
||||||
|
</ng-container>
|
||||||
<ion-option value="favourite" *ngIf="showFilters.favourite != 'hidden'" [disabled]="showFilters.favourite == 'disabled'">{{ 'addon.block_myoverview.favourites' | translate }}</ion-option>
|
<ion-option value="favourite" *ngIf="showFilters.favourite != 'hidden'" [disabled]="showFilters.favourite == 'disabled'">{{ 'addon.block_myoverview.favourites' | translate }}</ion-option>
|
||||||
<ion-option value="hidden" *ngIf="showFilters.hidden != 'hidden'" [disabled]="showFilters.hidden == 'disabled'">{{ 'addon.block_myoverview.hiddencourses' | translate }}</ion-option>
|
<ion-option value="hidden" *ngIf="showFilters.hidden != 'hidden'" [disabled]="showFilters.hidden == 'disabled'">{{ 'addon.block_myoverview.hiddencourses' | translate }}</ion-option>
|
||||||
</ion-select>
|
</ion-select>
|
||||||
</div>
|
</div>
|
||||||
<core-empty-box *ngIf="courses[selectedFilter].length == 0" image="assets/img/icons/courses.svg" [message]="'addon.block_myoverview.nocourses' | translate"></core-empty-box>
|
<core-empty-box *ngIf="filteredCourses.length == 0" image="assets/img/icons/courses.svg" [message]="'addon.block_myoverview.nocourses' | translate"></core-empty-box>
|
||||||
|
|
||||||
<!-- Filter courses. -->
|
<!-- Filter courses. -->
|
||||||
<ion-searchbar #searchbar *ngIf="showFilter" [(ngModel)]="courses.filter" (ionInput)="filterChanged($event)" (ionCancel)="filterChanged()" [placeholder]="'core.courses.filtermycourses' | translate">
|
<ion-searchbar #searchbar *ngIf="showFilter" [(ngModel)]="courses.filter" (ionInput)="filterChanged($event)" (ionCancel)="filterChanged()" [placeholder]="'core.courses.filtermycourses' | translate">
|
||||||
|
|
|
@ -43,12 +43,14 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
inprogress: [],
|
inprogress: [],
|
||||||
future: [],
|
future: [],
|
||||||
favourite: [],
|
favourite: [],
|
||||||
hidden: []
|
hidden: [],
|
||||||
|
custom: [], // Leave it empty to avoid download all those courses.
|
||||||
};
|
};
|
||||||
|
customFilter: any[] = [];
|
||||||
selectedFilter = 'inprogress';
|
selectedFilter = 'inprogress';
|
||||||
sort = 'fullname';
|
sort = 'fullname';
|
||||||
currentSite: any;
|
currentSite: any;
|
||||||
filteredCourses: any[];
|
filteredCourses: any[] = [];
|
||||||
prefetchCoursesData = {
|
prefetchCoursesData = {
|
||||||
all: {},
|
all: {},
|
||||||
allincludinghidden: {},
|
allincludinghidden: {},
|
||||||
|
@ -56,7 +58,8 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
past: {},
|
past: {},
|
||||||
future: {},
|
future: {},
|
||||||
favourite: {},
|
favourite: {},
|
||||||
hidden: {}
|
hidden: {},
|
||||||
|
custom: {}, // Leave it empty to avoid download all those courses.
|
||||||
};
|
};
|
||||||
showFilters = { // Options are show, disabled, hidden.
|
showFilters = { // Options are show, disabled, hidden.
|
||||||
all: 'show',
|
all: 'show',
|
||||||
|
@ -65,7 +68,8 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
inprogress: 'show',
|
inprogress: 'show',
|
||||||
future: 'show',
|
future: 'show',
|
||||||
favourite: 'show',
|
favourite: 'show',
|
||||||
hidden: 'show'
|
hidden: 'show',
|
||||||
|
custom: 'hidden', // True or false to show or hide.
|
||||||
};
|
};
|
||||||
showFilter = false;
|
showFilter = false;
|
||||||
showSelectorFilter = false;
|
showSelectorFilter = false;
|
||||||
|
@ -80,11 +84,15 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
protected courseIds = [];
|
protected courseIds = [];
|
||||||
protected fetchContentDefaultError = 'Error getting my overview data.';
|
protected fetchContentDefaultError = 'Error getting my overview data.';
|
||||||
|
|
||||||
constructor(injector: Injector, private coursesProvider: CoreCoursesProvider,
|
constructor(injector: Injector,
|
||||||
private courseCompletionProvider: AddonCourseCompletionProvider, private eventsProvider: CoreEventsProvider,
|
protected coursesProvider: CoreCoursesProvider,
|
||||||
private courseHelper: CoreCourseHelperProvider,
|
protected courseCompletionProvider: AddonCourseCompletionProvider,
|
||||||
private courseOptionsDelegate: CoreCourseOptionsDelegate, private coursesHelper: CoreCoursesHelperProvider,
|
protected eventsProvider: CoreEventsProvider,
|
||||||
private sitesProvider: CoreSitesProvider, private timeUtils: CoreTimeUtilsProvider) {
|
protected courseHelper: CoreCourseHelperProvider,
|
||||||
|
protected courseOptionsDelegate: CoreCourseOptionsDelegate,
|
||||||
|
protected coursesHelper: CoreCoursesHelperProvider,
|
||||||
|
protected sitesProvider: CoreSitesProvider,
|
||||||
|
protected timeUtils: CoreTimeUtilsProvider) {
|
||||||
|
|
||||||
super(injector, 'AddonBlockMyOverviewComponent');
|
super(injector, 'AddonBlockMyOverviewComponent');
|
||||||
}
|
}
|
||||||
|
@ -115,7 +123,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
this.sort = value;
|
this.sort = value;
|
||||||
}));
|
}));
|
||||||
promises.push(this.currentSite.getLocalSiteConfig('AddonBlockMyOverviewFilter', this.selectedFilter).then((value) => {
|
promises.push(this.currentSite.getLocalSiteConfig('AddonBlockMyOverviewFilter', this.selectedFilter).then((value) => {
|
||||||
this.selectedFilter = typeof this.courses[value] == 'undefined' ? 'inprogress' : value;
|
this.selectedFilter = value;
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Promise.all(promises).finally(() => {
|
Promise.all(promises).finally(() => {
|
||||||
|
@ -213,6 +221,15 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
(!config || config.displaygroupingstarred.value == '1'),
|
(!config || config.displaygroupingstarred.value == '1'),
|
||||||
this.courses.favourite.length === 0);
|
this.courses.favourite.length === 0);
|
||||||
|
|
||||||
|
this.showFilters.custom = this.getShowFilterValue(this.showSelectorFilter && config &&
|
||||||
|
config.displaygroupingcustomfield.value == '1' && config.customfieldsexport && config.customfieldsexport.value,
|
||||||
|
false);
|
||||||
|
if (this.showFilters.custom == 'show') {
|
||||||
|
this.customFilter = this.textUtils.parseJSON(config.customfieldsexport.value);
|
||||||
|
} else {
|
||||||
|
this.customFilter = [];
|
||||||
|
}
|
||||||
|
|
||||||
if (this.showSelectorFilter) {
|
if (this.showSelectorFilter) {
|
||||||
// Check if any selector is shown and not disabled.
|
// Check if any selector is shown and not disabled.
|
||||||
this.showSelectorFilter = Object.keys(this.showFilters).some((key) => {
|
this.showSelectorFilter = Object.keys(this.showFilters).some((key) => {
|
||||||
|
@ -224,7 +241,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
// No selector, or the default option is disabled, show all.
|
// No selector, or the default option is disabled, show all.
|
||||||
this.selectedFilter = 'all';
|
this.selectedFilter = 'all';
|
||||||
}
|
}
|
||||||
this.filteredCourses = this.courses[this.selectedFilter];
|
this.setCourseFilter(this.selectedFilter);
|
||||||
|
|
||||||
this.initPrefetchCoursesIcons();
|
this.initPrefetchCoursesIcons();
|
||||||
});
|
});
|
||||||
|
@ -248,17 +265,17 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
*/
|
*/
|
||||||
filterChanged(event: any): void {
|
filterChanged(event: any): void {
|
||||||
const newValue = event.target.value && event.target.value.trim().toLowerCase();
|
const newValue = event.target.value && event.target.value.trim().toLowerCase();
|
||||||
if (!newValue || !this.courses['all']) {
|
if (!newValue || this.courses.allincludinghidden.length <= 0) {
|
||||||
this.filteredCourses = this.courses['all'];
|
this.filteredCourses = this.courses.allincludinghidden;
|
||||||
} else {
|
} else {
|
||||||
// Use displayname if avalaible, or fullname if not.
|
// Use displayname if avalaible, or fullname if not.
|
||||||
if (this.courses['all'].length > 0 &&
|
if (this.courses.allincludinghidden.length > 0 &&
|
||||||
typeof this.courses['all'][0].displayname != 'undefined') {
|
typeof this.courses.allincludinghidden[0].displayname != 'undefined') {
|
||||||
this.filteredCourses = this.courses['all'].filter((course) => {
|
this.filteredCourses = this.courses.allincludinghidden.filter((course) => {
|
||||||
return course.displayname.toLowerCase().indexOf(newValue) > -1;
|
return course.displayname.toLowerCase().indexOf(newValue) > -1;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
this.filteredCourses = this.courses['all'].filter((course) => {
|
this.filteredCourses = this.courses.allincludinghidden.filter((course) => {
|
||||||
return course.fullname.toLowerCase().indexOf(newValue) > -1;
|
return course.fullname.toLowerCase().indexOf(newValue) > -1;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -304,8 +321,50 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
* The selected courses filter have changed.
|
* The selected courses filter have changed.
|
||||||
*/
|
*/
|
||||||
selectedChanged(): void {
|
selectedChanged(): void {
|
||||||
this.currentSite.setLocalSiteConfig('AddonBlockMyOverviewFilter', this.selectedFilter);
|
this.setCourseFilter(this.selectedFilter);
|
||||||
this.filteredCourses = this.courses[this.selectedFilter];
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set selected courses filter.
|
||||||
|
*
|
||||||
|
* @param {string} filter Filter name to set.
|
||||||
|
*/
|
||||||
|
protected setCourseFilter(filter: string): void {
|
||||||
|
this.selectedFilter = filter;
|
||||||
|
|
||||||
|
if (this.showFilters.custom == 'show' && filter.startsWith('custom-') &&
|
||||||
|
typeof this.customFilter[filter.substr(7)] != 'undefined') {
|
||||||
|
const filterName = this.block.configs.customfiltergrouping.value,
|
||||||
|
filterValue = this.customFilter[filter.substr(7)].value;
|
||||||
|
|
||||||
|
this.loaded = false;
|
||||||
|
|
||||||
|
this.coursesProvider.getEnrolledCoursesByCustomField(filterName, filterValue).then((courses) => {
|
||||||
|
// Get the courses information from allincludinghidden to get the max info about the course.
|
||||||
|
const courseIds = courses.map((course) => course.id);
|
||||||
|
this.filteredCourses = this.courses.allincludinghidden.filter((allCourse) =>
|
||||||
|
courseIds.indexOf(allCourse.id) !== -1);
|
||||||
|
}).catch((error) => {
|
||||||
|
this.domUtils.showErrorModalDefault(error, this.fetchContentDefaultError);
|
||||||
|
}).finally(() => {
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Only save the filter if not a custom one.
|
||||||
|
this.currentSite.setLocalSiteConfig('AddonBlockMyOverviewFilter', filter);
|
||||||
|
|
||||||
|
if (this.showFilters[filter] == 'show') {
|
||||||
|
this.filteredCourses = this.courses[filter];
|
||||||
|
} else {
|
||||||
|
const activeFilter = Object.keys(this.showFilters).find((name) => {
|
||||||
|
return this.showFilters[name] == 'show';
|
||||||
|
});
|
||||||
|
|
||||||
|
if (activeFilter) {
|
||||||
|
this.setCourseFilter(activeFilter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -314,6 +373,8 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
* @param courses Courses to filter.
|
* @param courses Courses to filter.
|
||||||
*/
|
*/
|
||||||
initCourseFilters(courses: any[]): void {
|
initCourseFilters(courses: any[]): void {
|
||||||
|
this.courses.allincludinghidden = courses;
|
||||||
|
|
||||||
if (this.showSortFilter) {
|
if (this.showSortFilter) {
|
||||||
if (this.sort == 'lastaccess') {
|
if (this.sort == 'lastaccess') {
|
||||||
courses.sort((a, b) => {
|
courses.sort((a, b) => {
|
||||||
|
@ -330,7 +391,6 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
}
|
}
|
||||||
|
|
||||||
this.courses.all = [];
|
this.courses.all = [];
|
||||||
this.courses.allincludinghidden = [];
|
|
||||||
this.courses.past = [];
|
this.courses.past = [];
|
||||||
this.courses.inprogress = [];
|
this.courses.inprogress = [];
|
||||||
this.courses.future = [];
|
this.courses.future = [];
|
||||||
|
@ -339,7 +399,6 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
|
|
||||||
const today = this.timeUtils.timestamp();
|
const today = this.timeUtils.timestamp();
|
||||||
courses.forEach((course) => {
|
courses.forEach((course) => {
|
||||||
this.courses.allincludinghidden.push(course);
|
|
||||||
if (course.hidden) {
|
if (course.hidden) {
|
||||||
this.courses.hidden.push(course);
|
this.courses.hidden.push(course);
|
||||||
} else {
|
} else {
|
||||||
|
@ -362,7 +421,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
this.filteredCourses = this.courses[this.selectedFilter];
|
this.setCourseFilter(this.selectedFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -373,7 +432,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
switchSort(sort: string): void {
|
switchSort(sort: string): void {
|
||||||
this.sort = sort;
|
this.sort = sort;
|
||||||
this.currentSite.setLocalSiteConfig('AddonBlockMyOverviewSort', this.sort);
|
this.currentSite.setLocalSiteConfig('AddonBlockMyOverviewSort', this.sort);
|
||||||
this.initCourseFilters(this.courses.all.concat(this.courses.hidden));
|
this.initCourseFilters(this.courses.allincludinghidden);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -382,7 +441,12 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
switchFilter(): void {
|
switchFilter(): void {
|
||||||
this.showFilter = !this.showFilter;
|
this.showFilter = !this.showFilter;
|
||||||
this.courses.filter = '';
|
this.courses.filter = '';
|
||||||
this.filteredCourses = this.courses[this.showFilter ? 'all' : this.selectedFilter];
|
|
||||||
|
if (this.showFilter) {
|
||||||
|
this.filteredCourses = this.courses.allincludinghidden;
|
||||||
|
} else {
|
||||||
|
this.setCourseFilter(this.selectedFilter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -402,7 +466,7 @@ export class AddonBlockMyOverviewComponent extends CoreBlockBaseComponent implem
|
||||||
* @return If switch button that enables the filter input is shown or not.
|
* @return If switch button that enables the filter input is shown or not.
|
||||||
*/
|
*/
|
||||||
showFilterSwitchButton(): boolean {
|
showFilterSwitchButton(): boolean {
|
||||||
return this.loaded && this.courses['all'] && this.courses['all'].length > 5;
|
return this.loaded && this.courses.allincludinghidden && this.courses.allincludinghidden.length > 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -514,6 +514,36 @@ export class CoreCoursesProvider {
|
||||||
return this.ROOT_CACHE_KEY + 'coursesbyfield:' + field + ':' + value;
|
return this.ROOT_CACHE_KEY + 'coursesbyfield:' + field + ':' + value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get courses matching the given custom field. Only works in online.
|
||||||
|
*
|
||||||
|
* @param customFieldName Custom field name.
|
||||||
|
* @param customFieldValue Custom field value.
|
||||||
|
* @param siteId Site ID. If not defined, current site.
|
||||||
|
* @return Promise resolved with the list of courses.
|
||||||
|
* @since 3.8
|
||||||
|
*/
|
||||||
|
getEnrolledCoursesByCustomField(customFieldName: string, customFieldValue: string, siteId?: string): Promise<any[]> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const data = {
|
||||||
|
classification: 'customfield',
|
||||||
|
customfieldname: customFieldName,
|
||||||
|
customfieldvalue: customFieldValue
|
||||||
|
},
|
||||||
|
preSets = {
|
||||||
|
getFromCache: false
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_course_get_enrolled_courses_by_timeline_classification', data, preSets).then((courses) => {
|
||||||
|
if (courses.courses) {
|
||||||
|
return courses.courses;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if get courses by field WS is available in a certain site.
|
* Check if get courses by field WS is available in a certain site.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue