From 661709acb47339b5dc11c6eb7c794b73ed62167f Mon Sep 17 00:00:00 2001 From: Dani Palou Date: Wed, 3 Jul 2019 08:13:02 +0200 Subject: [PATCH] MOBILE-3021 calendar: Support monthly view --- scripts/langindex.json | 15 ++ .../calendar/addon-calendar-calendar.html | 52 +++++ .../components/calendar/calendar.scss | 42 ++++ .../calendar/components/calendar/calendar.ts | 221 ++++++++++++++++++ .../calendar/components/components.module.ts | 40 ++++ src/addon/calendar/lang/en.json | 16 +- src/addon/calendar/pages/index/index.html | 3 +- .../calendar/pages/index/index.module.ts | 2 + src/addon/calendar/pages/index/index.ts | 159 ++++++++++++- src/addon/calendar/pages/list/list.ts | 48 +--- src/addon/calendar/providers/calendar.ts | 43 ++++ src/addon/calendar/providers/helper.ts | 74 +++++- src/assets/lang/en.json | 15 ++ src/lang/en.json | 1 + src/theme/variables.scss | 1 + 15 files changed, 678 insertions(+), 54 deletions(-) create mode 100644 src/addon/calendar/components/calendar/addon-calendar-calendar.html create mode 100644 src/addon/calendar/components/calendar/calendar.scss create mode 100644 src/addon/calendar/components/calendar/calendar.ts create mode 100644 src/addon/calendar/components/components.module.ts diff --git a/scripts/langindex.json b/scripts/langindex.json index 40cfc905d..2f79cd0a0 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -106,9 +106,13 @@ "addon.calendar.eventname": "calendar", "addon.calendar.eventstarttime": "calendar", "addon.calendar.eventtype": "calendar", + "addon.calendar.fri": "calendar", + "addon.calendar.friday": "calendar", "addon.calendar.gotoactivity": "calendar", "addon.calendar.invalidtimedurationminutes": "calendar", "addon.calendar.invalidtimedurationuntil": "calendar", + "addon.calendar.mon": "calendar", + "addon.calendar.monday": "calendar", "addon.calendar.newevent": "calendar", "addon.calendar.noevents": "local_moodlemobileapp", "addon.calendar.nopermissiontoupdatecalendar": "error", @@ -118,7 +122,15 @@ "addon.calendar.repeateditthis": "calendar", "addon.calendar.repeatevent": "calendar", "addon.calendar.repeatweeksl": "calendar", + "addon.calendar.sat": "calendar", + "addon.calendar.saturday": "calendar", "addon.calendar.setnewreminder": "local_moodlemobileapp", + "addon.calendar.sun": "calendar", + "addon.calendar.sunday": "calendar", + "addon.calendar.thu": "calendar", + "addon.calendar.thursday": "calendar", + "addon.calendar.tue": "calendar", + "addon.calendar.tuesday": "calendar", "addon.calendar.typecategory": "calendar", "addon.calendar.typeclose": "calendar", "addon.calendar.typecourse": "calendar", @@ -128,6 +140,8 @@ "addon.calendar.typeopen": "calendar", "addon.calendar.typesite": "calendar", "addon.calendar.typeuser": "calendar", + "addon.calendar.wed": "calendar", + "addon.calendar.wednesday": "calendar", "addon.competency.activities": "tool_lp", "addon.competency.competencies": "competency", "addon.competency.competenciesmostoftennotproficientincourse": "tool_lp", @@ -1657,6 +1671,7 @@ "core.notingroup": "moodle", "core.notsent": "local_moodlemobileapp", "core.now": "moodle", + "core.nummore": "local_moodlemobileapp", "core.numwords": "moodle", "core.offline": "message", "core.ok": "moodle", diff --git a/src/addon/calendar/components/calendar/addon-calendar-calendar.html b/src/addon/calendar/components/calendar/addon-calendar-calendar.html new file mode 100644 index 000000000..04d35dfda --- /dev/null +++ b/src/addon/calendar/components/calendar/addon-calendar-calendar.html @@ -0,0 +1,52 @@ + + + + + + + + + + +

{{ periodName }}

+
+ + + + + +
+
+ + + + + + +

{{ day.shortname | translate }}

+
+
+ + + + + +

{{ day.mday }}

+ + +

+ + +
+

+ + {{event.name}} +

+

{{ 'core.nummore' | translate:{$a: day.filteredEvents.length - 3} }}

+
+
+ +
+
+ +
diff --git a/src/addon/calendar/components/calendar/calendar.scss b/src/addon/calendar/components/calendar/calendar.scss new file mode 100644 index 000000000..853cff23a --- /dev/null +++ b/src/addon/calendar/components/calendar/calendar.scss @@ -0,0 +1,42 @@ + +$calendar-event-category-color: $purple !default; // Purple. +$calendar-event-course-color: $red !default; // Red. +$calendar-event-group-color: $yellow !default; // Yellow. +$calendar-event-user-color: $blue !default; // Blue. +$calendar-event-site-color: $green !default; // Green. + +ion-app.app-root addon-calendar-calendar { + + .addon-calendar-weekdays { + opacity: 0.4; + } + + .addon-calendar-day-events { + @include text-align('start'); + } + + .calendar_event_type { + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + border: 1px solid white; + @include margin-horizontal(1px, 1px); + + &.calendar_event_category { + background-color: $calendar-event-category-color; + } + &.calendar_event_course { + background-color: $calendar-event-course-color; + } + &.calendar_event_group { + background-color: $calendar-event-group-color; + } + &.calendar_event_user { + background-color: $calendar-event-user-color; + } + &.calendar_event_site { + background-color: $calendar-event-site-color; + } + } +} \ No newline at end of file diff --git a/src/addon/calendar/components/calendar/calendar.ts b/src/addon/calendar/components/calendar/calendar.ts new file mode 100644 index 000000000..9bb41721e --- /dev/null +++ b/src/addon/calendar/components/calendar/calendar.ts @@ -0,0 +1,221 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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, OnDestroy, OnInit, Input, OnChanges, SimpleChange } from '@angular/core'; +import { CoreEventsProvider } from '@providers/events'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreTimeUtilsProvider } from '@providers/utils/time'; +import { CoreUtilsProvider } from '@providers/utils/utils'; +import { AddonCalendarProvider } from '../../providers/calendar'; +import { AddonCalendarHelperProvider } from '../../providers/helper'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; + +/** + * Component that displays a calendar. + */ +@Component({ + selector: 'addon-calendar-calendar', + templateUrl: 'addon-calendar-calendar.html', +}) +export class AddonCalendarCalendarComponent implements OnInit, OnChanges, OnDestroy { + @Input() initialYear: number | string; // Initial year to load. + @Input() initialMonth: number | string; // Initial month to load. + @Input() courseId: number | string; + @Input() categoryId: number | string; // Category ID the course belongs to. + @Input() canNavigate?: string | boolean; // Whether to include arrows to change the month. Defaults to true. + + periodName: string; + weekDays: any[]; + weeks: any[]; + loaded = false; + + protected year: number; + protected month: number; + protected categoriesRetrieved = false; + protected categories = {}; + + constructor(eventsProvider: CoreEventsProvider, + sitesProvider: CoreSitesProvider, + private calendarProvider: AddonCalendarProvider, + private calendarHelper: AddonCalendarHelperProvider, + private domUtils: CoreDomUtilsProvider, + private timeUtils: CoreTimeUtilsProvider, + private utils: CoreUtilsProvider, + private coursesProvider: CoreCoursesProvider) { + + } + + /** + * Component loaded. + */ + ngOnInit(): void { + const now = new Date(); + + this.year = this.initialYear ? Number(this.initialYear) : now.getFullYear(); + this.month = this.initialYear ? Number(this.initialYear) : now.getMonth() + 1; + this.canNavigate = typeof this.canNavigate == 'undefined' ? true : this.utils.isTrueOrOne(this.canNavigate); + + this.fetchData(); + } + + /** + * Detect changes on input properties. + */ + ngOnChanges(changes: {[name: string]: SimpleChange}): void { + + if ((changes.courseId || changes.categoryId) && this.weeks) { + const courseId = this.courseId ? Number(this.courseId) : undefined, + categoryId = this.categoryId ? Number(this.categoryId) : undefined; + + this.filterEvents(courseId, categoryId); + } + } + + /** + * Fetch contacts. + * + * @param {boolean} [refresh=false] True if we are refreshing contacts, false if we are loading more. + * @return {Promise} Promise resolved when done. + */ + fetchData(refresh: boolean = false): Promise { + const courseId = this.courseId ? Number(this.courseId) : undefined, + categoryId = this.categoryId ? Number(this.categoryId) : undefined, + promises = []; + + promises.push(this.loadCategories()); + + promises.push(this.calendarProvider.getMonthlyEvents(this.year, this.month, courseId, categoryId).then((result) => { + + // Calculate the period name. We don't use the one in result because it's in server's language. + this.periodName = this.timeUtils.userDate(new Date(this.year, this.month - 1).getTime(), 'core.strftimemonthyear'); + + this.weekDays = this.calendarProvider.getWeekDays(result.daynames[0].dayno); + this.weeks = result.weeks; + + this.filterEvents(courseId, categoryId); + })); + + return Promise.all(promises).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); + }).finally(() => { + this.loaded = true; + }); + } + + /** + * Load categories to be able to filter events. + * + * @return {Promise} Promise resolved when done. + */ + protected loadCategories(): Promise { + if (this.categoriesRetrieved) { + // Already retrieved, stop. + return Promise.resolve(); + } + + return this.coursesProvider.getCategories(0, true).then((cats) => { + this.categoriesRetrieved = true; + this.categories = {}; + + // Index categories by ID. + cats.forEach((category) => { + this.categories[category.id] = category; + }); + }).catch(() => { + // Ignore errors. + }); + } + + /** + * Filter events to only display events belonging to a certain course. + * + * @param {number} courseId Course ID. + * @param {number} categoryId Category the course belongs to. + */ + filterEvents(courseId: number, categoryId: number): void { + + this.weeks.forEach((week) => { + week.days.forEach((day) => { + if (!courseId || courseId < 0) { + day.filteredEvents = day.events; + } else { + day.filteredEvents = day.events.filter((event) => { + return this.calendarHelper.shouldDisplayEvent(event, courseId, categoryId, this.categories); + }); + } + + // Re-calculate some properties. + this.calendarHelper.calculateDayData(day, day.filteredEvents); + }); + }); + } + + /** + * Refresh events. + * + * @return {Promise} Promise resolved when done. + */ + refreshData(): Promise { + const promises = []; + + promises.push(this.calendarProvider.invalidateMonthlyEvents(this.year, this.month)); + promises.push(this.coursesProvider.invalidateCategories(0, true)); + + this.categoriesRetrieved = false; // Get categories again. + + return Promise.all(promises).then(() => { + return this.fetchData(true); + }); + } + + /** + * Load next month. + */ + loadNext(): void { + if (this.month === 12) { + this.month = 1; + this.year++; + } else { + this.month++; + } + + this.loaded = false; + + this.fetchData(); + } + + /** + * Load previous month. + */ + loadPrevious(): void { + if (this.month === 1) { + this.month = 12; + this.year--; + } else { + this.month--; + } + + this.loaded = false; + + this.fetchData(); + } + + /** + * Component destroyed. + */ + ngOnDestroy(): void { + // @todo + } +} diff --git a/src/addon/calendar/components/components.module.ts b/src/addon/calendar/components/components.module.ts new file mode 100644 index 000000000..a6d5afe22 --- /dev/null +++ b/src/addon/calendar/components/components.module.ts @@ -0,0 +1,40 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// 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 { CommonModule } from '@angular/common'; +import { IonicModule } from 'ionic-angular'; +import { TranslateModule } from '@ngx-translate/core'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { AddonCalendarCalendarComponent } from '../components/calendar/calendar'; + +@NgModule({ + declarations: [ + AddonCalendarCalendarComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule, + CoreDirectivesModule + ], + providers: [ + ], + exports: [ + AddonCalendarCalendarComponent + ] +}) +export class AddonCalendarComponentsModule {} diff --git a/src/addon/calendar/lang/en.json b/src/addon/calendar/lang/en.json index 8792b48c5..412c81742 100644 --- a/src/addon/calendar/lang/en.json +++ b/src/addon/calendar/lang/en.json @@ -22,9 +22,13 @@ "eventname": "Event title", "eventstarttime": "Start time", "eventtype": "Event type", + "fri": "Fri", + "friday": "Friday", "gotoactivity": "Go to activity", "invalidtimedurationminutes": "The duration in minutes you have entered is invalid. Please enter the duration in minutes greater than 0 or select no duration.", "invalidtimedurationuntil": "The date and time you selected for duration until is before the start time of the event. Please correct this before proceeding.", + "mon": "Mon", + "monday": "Monday", "newevent": "New event", "noevents": "There are no events", "nopermissiontoupdatecalendar": "Sorry, but you do not have permission to update the calendar event", @@ -34,7 +38,15 @@ "repeateditthis": "Apply changes to this event only", "repeatevent": "Repeat this event", "repeatweeksl": "Repeat weekly, creating altogether", + "sat": "Sat", + "saturday": "Saturday", "setnewreminder": "Set a new reminder", + "sun": "Sun", + "sunday": "Sunday", + "thu": "Thu", + "thursday": "Thursday", + "tue": "Tue", + "tuesday": "Tuesday", "typeclose": "Close event", "typecourse": "Course event", "typecategory": "Category event", @@ -43,5 +55,7 @@ "typegroup": "Group event", "typeopen": "Open event", "typesite": "Site event", - "typeuser": "User event" + "typeuser": "User event", + "wed": "Wed", + "wednesday": "Wednesday" } \ No newline at end of file diff --git a/src/addon/calendar/pages/index/index.html b/src/addon/calendar/pages/index/index.html index 122436442..42c3cd4b9 100644 --- a/src/addon/calendar/pages/index/index.html +++ b/src/addon/calendar/pages/index/index.html @@ -15,9 +15,8 @@ - - + diff --git a/src/addon/calendar/pages/index/index.module.ts b/src/addon/calendar/pages/index/index.module.ts index 0c3486dd9..bf925e799 100644 --- a/src/addon/calendar/pages/index/index.module.ts +++ b/src/addon/calendar/pages/index/index.module.ts @@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core'; import { CoreComponentsModule } from '@components/components.module'; import { CoreDirectivesModule } from '@directives/directives.module'; import { CorePipesModule } from '@pipes/pipes.module'; +import { AddonCalendarComponentsModule } from '../../components/components.module'; import { AddonCalendarIndexPage } from './index'; @NgModule({ @@ -28,6 +29,7 @@ import { AddonCalendarIndexPage } from './index'; CoreComponentsModule, CoreDirectivesModule, CorePipesModule, + AddonCalendarComponentsModule, IonicPageModule.forChild(AddonCalendarIndexPage), TranslateModule.forChild() ], diff --git a/src/addon/calendar/pages/index/index.ts b/src/addon/calendar/pages/index/index.ts index 3c1be5ed2..9ab19cd7e 100644 --- a/src/addon/calendar/pages/index/index.ts +++ b/src/addon/calendar/pages/index/index.ts @@ -8,12 +8,20 @@ // // 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. +// WITHOUT WARRANTIES OR CONDITIONS OFx ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -import { Component, OnInit } from '@angular/core'; -import { IonicPage } from 'ionic-angular'; +import { Component, OnInit, ViewChild } from '@angular/core'; +import { IonicPage, NavParams, NavController, PopoverController } from 'ionic-angular'; +import { CoreLocalNotificationsProvider } from '@providers/local-notifications'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { AddonCalendarProvider } from '../../providers/calendar'; +import { AddonCalendarHelperProvider } from '../../providers/helper'; +import { AddonCalendarCalendarComponent } from '../../components/calendar/calendar'; +import { CoreCoursesProvider } from '@core/courses/providers/courses'; +import { CoreCoursePickerMenuPopoverComponent } from '@components/course-picker-menu/course-picker-menu-popover'; +import { TranslateService } from '@ngx-translate/core'; /** * Page that displays the calendar events. @@ -24,15 +32,154 @@ import { IonicPage } from 'ionic-angular'; templateUrl: 'index.html', }) export class AddonCalendarIndexPage implements OnInit { + @ViewChild(AddonCalendarCalendarComponent) calendarComponent: AddonCalendarCalendarComponent; - constructor() { - // @todo + protected allCourses = { + id: -1, + fullname: this.translate.instant('core.fulllistofcourses'), + category: -1 + }; + + courseId: number; + categoryId: number; + canCreate = false; + courses: any[]; + notificationsEnabled = false; + loaded = false; + + constructor(localNotificationsProvider: CoreLocalNotificationsProvider, + navParams: NavParams, + private navCtrl: NavController, + private domUtils: CoreDomUtilsProvider, + private calendarProvider: AddonCalendarProvider, + private calendarHelper: AddonCalendarHelperProvider, + private translate: TranslateService, + private coursesProvider: CoreCoursesProvider, + private popoverCtrl: PopoverController) { + + this.courseId = navParams.get('courseId'); + this.notificationsEnabled = localNotificationsProvider.isAvailable(); } /** * View loaded. */ ngOnInit(): void { - // @todo + this.fetchData(); + } + + /** + * Fetch all the data required for the view. + * + * @return {Promise} Promise resolved when done. + */ + fetchData(): Promise { + const promises = []; + + // Load courses for the popover. + promises.push(this.coursesProvider.getUserCourses(false).then((courses) => { + // Add "All courses". + courses.unshift(this.allCourses); + this.courses = courses; + + if (this.courseId) { + // Search the course to get the category. + const course = this.courses.find((course) => { + return course.id == this.courseId; + }); + + if (course) { + this.categoryId = course.category; + } + } + })); + + // Check if user can create events. + promises.push(this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => { + this.canCreate = canEdit; + })); + + return Promise.all(promises).catch((error) => { + this.domUtils.showErrorModalDefault(error, 'addon.calendar.errorloadevents', true); + }).finally(() => { + this.loaded = true; + }); + } + + /** + * Refresh the data. + * + * @param {any} [refresher] Refresher. + * @return {Promise} Promise resolved when done. + */ + doRefresh(refresher?: any): void { + if (!this.loaded) { + return; + } + + const promises = []; + + promises.push(this.calendarProvider.invalidateAllowedEventTypes().then(() => { + return this.fetchData(); + })); + + // Refresh the sub-component. + promises.push(this.calendarComponent.refreshData()); + + Promise.all(promises).finally(() => { + refresher && refresher.complete(); + }); + } + + /** + * Show the context menu. + * + * @param {MouseEvent} event Event. + */ + openCourseFilter(event: MouseEvent): void { + const popover = this.popoverCtrl.create(CoreCoursePickerMenuPopoverComponent, { + courses: this.courses, + courseId: this.courseId + }); + + popover.onDidDismiss((course) => { + if (course) { + this.courseId = course.id > 0 ? course.id : undefined; + this.categoryId = course.id > 0 ? course.category : undefined; + + // Course viewed has changed, check if the user can create events for this course calendar. + this.calendarHelper.canEditEvents(this.courseId).then((canEdit) => { + this.canCreate = canEdit; + }); + } + }); + popover.present({ + ev: event + }); + } + + /** + * Open page to create/edit an event. + * + * @param {number} [eventId] Event ID to edit. + */ + openEdit(eventId?: number): void { + const params: any = {}; + + if (eventId) { + params.eventId = eventId; + } + if (this.courseId) { + params.courseId = this.courseId; + } + + this.navCtrl.push('AddonCalendarEditEventPage', params); + } + + /** + * Open calendar events settings. + */ + openSettings(): void { + this.navCtrl.push('AddonCalendarSettingsPage'); } } diff --git a/src/addon/calendar/pages/list/list.ts b/src/addon/calendar/pages/list/list.ts index 0dac7f74b..00c6657d4 100644 --- a/src/addon/calendar/pages/list/list.ts +++ b/src/addon/calendar/pages/list/list.ts @@ -423,50 +423,10 @@ export class AddonCalendarListPage implements OnDestroy { return this.events; } - return this.events.filter(this.shouldDisplayEvent.bind(this)); - } - - /** - * Check if an event should be displayed based on the filter. - * - * @param {any} event Event object. - * @return {boolean} Whether it should be displayed. - */ - protected shouldDisplayEvent(event: any): boolean { - if (event.eventtype == 'user' || event.eventtype == 'site') { - // User or site event, display it. - return true; - } - - if (event.eventtype == 'category') { - if (!event.categoryid || !Object.keys(this.categories).length) { - // We can't tell if the course belongs to the category, display them all. - return true; - } - if (event.categoryid == this.filter.course.category) { - // The event is in the same category as the course, display it. - return true; - } - - // Check parent categories. - let category = this.categories[this.filter.course.category]; - while (category) { - if (!category.parent) { - // Category doesn't have parent, stop. - break; - } - - if (event.categoryid == category.parent) { - return true; - } - category = this.categories[category.parent]; - } - - return false; - } - - // Show the event if it is from site home or if it matches the selected course. - return event.courseid === this.siteHomeId || event.courseid == this.filter.course.id; + return this.events.filter((event) => { + return this.calendarHelper.shouldDisplayEvent(event, this.filter.course.id, this.filter.course.category, + this.categories); + }); } /** diff --git a/src/addon/calendar/providers/calendar.ts b/src/addon/calendar/providers/calendar.ts index fdc4905d3..4705a8ad8 100644 --- a/src/addon/calendar/providers/calendar.ts +++ b/src/addon/calendar/providers/calendar.ts @@ -51,6 +51,37 @@ export class AddonCalendarProvider { static TYPE_USER = 'user'; protected ROOT_CACHE_KEY = 'mmaCalendar:'; + protected weekDays = [ + { + shortname: 'addon.calendar.sun', + fullname: 'addon.calendar.sunday' + }, + { + shortname: 'addon.calendar.mon', + fullname: 'addon.calendar.monday' + }, + { + shortname: 'addon.calendar.tue', + fullname: 'addon.calendar.tuesday' + }, + { + shortname: 'addon.calendar.wed', + fullname: 'addon.calendar.wednesday' + }, + { + shortname: 'addon.calendar.thu', + fullname: 'addon.calendar.thursday' + }, + { + shortname: 'addon.calendar.fri', + fullname: 'addon.calendar.friday' + }, + { + shortname: 'addon.calendar.sat', + fullname: 'addon.calendar.saturday' + } + ]; + // Variables for database. static EVENTS_TABLE = 'addon_calendar_events_2'; static REMINDERS_TABLE = 'addon_calendar_reminders'; @@ -875,6 +906,18 @@ export class AddonCalendarProvider { return this.getUpcomingEventsPrefixCacheKey() + (courseId ? courseId : '') + ':' + (categoryId ? categoryId : ''); } + /** + * Get the week days, already ordered according to a specified starting day. + * + * @param {number} [startingDay=0] Starting day. 0=Sunday, 1=Monday, ... + * @return {any[]} Week days. + */ + getWeekDays(startingDay?: number): any[] { + startingDay = startingDay || 0; + + return this.weekDays.slice(startingDay).concat(this.weekDays.slice(0, startingDay)); + } + /** * Invalidates access information. * diff --git a/src/addon/calendar/providers/helper.ts b/src/addon/calendar/providers/helper.ts index ae857225d..94cfa1e93 100644 --- a/src/addon/calendar/providers/helper.ts +++ b/src/addon/calendar/providers/helper.ts @@ -14,6 +14,7 @@ import { Injectable } from '@angular/core'; import { CoreLoggerProvider } from '@providers/logger'; +import { CoreSitesProvider } from '@providers/sites'; import { CoreCourseProvider } from '@core/course/providers/course'; import { AddonCalendarProvider } from './calendar'; import { CoreConstants } from '@core/constants'; @@ -33,11 +34,35 @@ export class AddonCalendarHelperProvider { category: 'fa-cubes' }; - constructor(logger: CoreLoggerProvider, private courseProvider: CoreCourseProvider, + constructor(logger: CoreLoggerProvider, + private courseProvider: CoreCourseProvider, + private sitesProvider: CoreSitesProvider, private calendarProvider: AddonCalendarProvider) { this.logger = logger.getInstance('AddonCalendarHelperProvider'); } + /** + * Calculate some day data based on a list of events for that day. + * + * @param {any} day Day. + * @param {any[]} events Events. + */ + calculateDayData(day: any, events: any[]): void { + day.hasevents = events.length > 0; + day.haslastdayofevent = false; + + const types = {}; + events.forEach((event) => { + types[event.eventtype] = true; + + if (event.islastday) { + day.haslastdayofevent = true; + } + }); + + day.calendareventtypes = Object.keys(types); + } + /** * Check if current user can create/edit events. * @@ -155,4 +180,51 @@ export class AddonCalendarHelperProvider { return false; } + + /** + * Check if an event should be displayed based on the filter. + * + * @param {any} event Event object. + * @param {number} courseId Course ID to filter. + * @param {number} categoryId Category ID the course belongs to. + * @param {any} categories Categories indexed by ID. + * @return {boolean} Whether it should be displayed. + */ + shouldDisplayEvent(event: any, courseId: number, categoryId: number, categories: any): boolean { + if (event.eventtype == 'user' || event.eventtype == 'site') { + // User or site event, display it. + return true; + } + + if (event.eventtype == 'category') { + if (!event.categoryid || !Object.keys(categories).length) { + // We can't tell if the course belongs to the category, display them all. + return true; + } + + if (event.categoryid == categoryId) { + // The event is in the same category as the course, display it. + return true; + } + + // Check parent categories. + let category = categories[categoryId]; + while (category) { + if (!category.parent) { + // Category doesn't have parent, stop. + break; + } + + if (event.categoryid == category.parent) { + return true; + } + category = categories[category.parent]; + } + + return false; + } + + // Show the event if it is from site home or if it matches the selected course. + return event.courseid === this.sitesProvider.getSiteHomeId() || event.courseid == courseId; + } } diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 19cb0008d..3e4a0a984 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -106,9 +106,13 @@ "addon.calendar.eventname": "Event title", "addon.calendar.eventstarttime": "Start time", "addon.calendar.eventtype": "Event type", + "addon.calendar.fri": "Fri", + "addon.calendar.friday": "Friday", "addon.calendar.gotoactivity": "Go to activity", "addon.calendar.invalidtimedurationminutes": "The duration in minutes you have entered is invalid. Please enter the duration in minutes greater than 0 or select no duration.", "addon.calendar.invalidtimedurationuntil": "The date and time you selected for duration until is before the start time of the event. Please correct this before proceeding.", + "addon.calendar.mon": "Mon", + "addon.calendar.monday": "Monday", "addon.calendar.newevent": "New event", "addon.calendar.noevents": "There are no events", "addon.calendar.nopermissiontoupdatecalendar": "Sorry, but you do not have permission to update the calendar event", @@ -118,7 +122,15 @@ "addon.calendar.repeateditthis": "Apply changes to this event only", "addon.calendar.repeatevent": "Repeat this event", "addon.calendar.repeatweeksl": "Repeat weekly, creating altogether", + "addon.calendar.sat": "Sat", + "addon.calendar.saturday": "Saturday", "addon.calendar.setnewreminder": "Set a new reminder", + "addon.calendar.sun": "Sun", + "addon.calendar.sunday": "Sunday", + "addon.calendar.thu": "Thu", + "addon.calendar.thursday": "Thursday", + "addon.calendar.tue": "Tue", + "addon.calendar.tuesday": "Tuesday", "addon.calendar.typecategory": "Category event", "addon.calendar.typeclose": "Close event", "addon.calendar.typecourse": "Course event", @@ -128,6 +140,8 @@ "addon.calendar.typeopen": "Open event", "addon.calendar.typesite": "Site event", "addon.calendar.typeuser": "User event", + "addon.calendar.wed": "Wed", + "addon.calendar.wednesday": "Wednesday", "addon.competency.activities": "Activities", "addon.competency.competencies": "Competencies", "addon.competency.competenciesmostoftennotproficientincourse": "Competencies most often not proficient in this course", @@ -1658,6 +1672,7 @@ "core.notingroup": "Sorry, but you need to be part of a group to see this page.", "core.notsent": "Not sent", "core.now": "now", + "core.nummore": "{{$a}} more", "core.numwords": "{{$a}} words", "core.offline": "Offline", "core.ok": "OK", diff --git a/src/lang/en.json b/src/lang/en.json index 4ec6a60a4..4f1d092cf 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -184,6 +184,7 @@ "notingroup": "Sorry, but you need to be part of a group to see this page.", "notsent": "Not sent", "now": "now", + "nummore": "{{$a}} more", "numwords": "{{$a}} words", "offline": "Offline", "ok": "OK", diff --git a/src/theme/variables.scss b/src/theme/variables.scss index 919afa355..0062ebe09 100644 --- a/src/theme/variables.scss +++ b/src/theme/variables.scss @@ -28,6 +28,7 @@ $green: #5e8100; // Accent. $red: #cb3d4d; $orange: #f98012; // Accent (never text). $yellow: #fbad1a; // Accent (never text). +$purple: #8e24aa; // Accent (never text). $core-color: $orange; // Branded apps customization