diff --git a/scripts/langindex.json b/scripts/langindex.json index a9cf44b1a..c0dc480fb 100644 --- a/scripts/langindex.json +++ b/scripts/langindex.json @@ -17,11 +17,14 @@ "addon.block_myoverview.nocoursesinprogress": "block_myoverview", "addon.block_myoverview.nocoursespast": "block_myoverview", "addon.block_myoverview.past": "block_myoverview", + "addon.block_timeline.duedate": "block_timeline", "addon.block_timeline.next30days": "block_timeline", + "addon.block_timeline.next3months": "block_timeline", + "addon.block_timeline.next6months": "block_timeline", "addon.block_timeline.next7days": "block_timeline", "addon.block_timeline.nocoursesinprogress": "block_timeline", "addon.block_timeline.noevents": "block_timeline", - "addon.block_timeline.recentlyoverdue": "local_moodlemobileapp", + "addon.block_timeline.overdue": "block_timeline", "addon.block_timeline.sortbycourses": "block_timeline", "addon.block_timeline.sortbydates": "block_timeline", "addon.calendar.calendar": "calendar", @@ -1058,6 +1061,7 @@ "core.accounts": "admin", "core.add": "moodle", "core.agelocationverification": "moodle", + "core.all": "moodle", "core.allparticipants": "moodle", "core.android": "local_moodlemobileapp", "core.answer": "moodle", @@ -1139,7 +1143,6 @@ "core.courses.cannotretrievemorecategories": "local_moodlemobileapp", "core.courses.categories": "moodle", "core.courses.confirmselfenrol": "local_moodlemobileapp", - "core.courses.courseoverview": "moodle", "core.courses.courses": "moodle", "core.courses.downloadcourses": "local_moodlemobileapp", "core.courses.enrolme": "local_moodlemobileapp", @@ -1150,6 +1153,7 @@ "core.courses.filtermycourses": "local_moodlemobileapp", "core.courses.frontpage": "admin", "core.courses.mycourses": "moodle", + "core.courses.mymoodle": "admin", "core.courses.nocourses": "my", "core.courses.nocoursesyet": "moodle", "core.courses.nosearchresults": "wiki", diff --git a/scripts/moodle_to_json.php b/scripts/moodle_to_json.php index 923581522..2eccc8c1d 100644 --- a/scripts/moodle_to_json.php +++ b/scripts/moodle_to_json.php @@ -279,6 +279,7 @@ function save_key($key, $value, $path) { $value = html_entity_decode($value); if ($file[$key] != $value) { $file[$key] = $value; + ksort($file); file_put_contents($filePath, str_replace('\/', '/', json_encode($file, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT))); } } diff --git a/src/addon/block/myoverview/component/addon-block-myoverview.html b/src/addon/block/myoverview/component/addon-block-myoverview.html index b580b1b1a..2212cc77a 100644 --- a/src/addon/block/myoverview/component/addon-block-myoverview.html +++ b/src/addon/block/myoverview/component/addon-block-myoverview.html @@ -7,14 +7,14 @@ -
- +
+ {{ 'addon.block_myoverview.inprogress' | translate }} {{ 'addon.block_myoverview.future' | translate }} {{ 'addon.block_myoverview.past' | translate }} -
+
diff --git a/src/addon/block/myoverview/component/myoverview.ts b/src/addon/block/myoverview/component/myoverview.ts index 92660870a..b3db61f7e 100644 --- a/src/addon/block/myoverview/component/myoverview.ts +++ b/src/addon/block/myoverview/component/myoverview.ts @@ -49,6 +49,7 @@ export class AddonBlockMyOverviewComponent extends AddonBlockComponent implement future: {} }; showFilter = false; + showSelectorFilter = false; protected prefetchIconsInitialized = false; protected isDestroyed; @@ -157,6 +158,7 @@ export class AddonBlockMyOverviewComponent extends AddonBlockComponent implement this.courses.filter = ''; this.showFilter = false; + this.showSelectorFilter = (this.courses.past.length + this.courses.future.length) > 0; this.filteredCourses = this.courses[this.selectedFilter]; this.initPrefetchCoursesIcons(); diff --git a/src/addon/block/timeline/components/events/addon-block-timeline-events.html b/src/addon/block/timeline/components/events/addon-block-timeline-events.html index 40f8e945c..8e25b1f51 100644 --- a/src/addon/block/timeline/components/events/addon-block-timeline-events.html +++ b/src/addon/block/timeline/components/events/addon-block-timeline-events.html @@ -1,49 +1,25 @@ - - - -

-

{{event.timesort * 1000 | coreFormatDate:"dfmediumdate" }}

- - {{event.action.itemcount}} -
-
- - - {{ 'addon.block_timeline.recentlyoverdue' | translate }} - - - - - - - {{ 'addon.block_timeline.next7days' | translate }} - - - - - - - {{ 'addon.block_timeline.next30days' | translate }} - - - - - - - {{ 'addon.block_myoverview.future' | translate }} - - + + +

{{ dayEvents.dayTimestamp * 1000 | coreFormatDate:"LL" }}

+
+ + + +

+

+ +

+ {{event.timesort * 1000 | coreFormatDate:"LT" }} +
- +
- - + diff --git a/src/addon/block/timeline/components/events/events.ts b/src/addon/block/timeline/components/events/events.ts index fdfdd9da1..c4d1f413d 100644 --- a/src/addon/block/timeline/components/events/events.ts +++ b/src/addon/block/timeline/components/events/events.ts @@ -17,6 +17,7 @@ import { NavController } from 'ionic-angular'; import { CoreSitesProvider } from '@providers/sites'; import { CoreDomUtilsProvider } from '@providers/utils/dom'; import { CoreTextUtilsProvider } from '@providers/utils/text'; +import { CoreTimeUtilsProvider } from '@providers/utils/time'; import { CoreUtilsProvider } from '@providers/utils/utils'; import { CoreCourseProvider } from '@core/course/providers/course'; import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper'; @@ -30,23 +31,21 @@ import * as moment from 'moment'; templateUrl: 'addon-block-timeline-events.html' }) export class AddonBlockTimelineEventsComponent implements OnChanges { - @Input() events: any[]; // The events to render. + @Input() events = []; // The events to render. @Input() showCourse?: boolean | string; // Whether to show the course name. + @Input() from: number; // Number of days from today to offset the events. + @Input() to?: number; // Number of days from today to limit the events to. If not defined, no limit. @Input() canLoadMore?: boolean; // Whether more events can be loaded. @Output() loadMore: EventEmitter; // Notify that more events should be loaded. empty: boolean; loadingMore: boolean; - recentlyOverdue: any[] = []; - today: any[] = []; - next7Days: any[] = []; - next30Days: any[] = []; - future: any[] = []; + filteredEvents = []; constructor(@Optional() private navCtrl: NavController, private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider, private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider, - private contentLinksHelper: CoreContentLinksHelperProvider) { + private contentLinksHelper: CoreContentLinksHelperProvider, private timeUtils: CoreTimeUtilsProvider) { this.loadMore = new EventEmitter(); } @@ -56,8 +55,34 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { ngOnChanges(changes: {[name: string]: SimpleChange}): void { this.showCourse = this.utils.isTrueOrOne(this.showCourse); - if (changes.events) { - this.updateEvents(); + if (changes.events || changes.from || changes.to) { + if (this.events && this.events.length > 0) { + const filteredEvents = this.filterEventsByTime(this.from, this.to); + this.empty = !filteredEvents || filteredEvents.length <= 0; + + const eventsByDay = {}; + filteredEvents.forEach((event) => { + const dayTimestamp = this.timeUtils.getMidnightForTimestamp(event.timesort); + if (eventsByDay[dayTimestamp]) { + eventsByDay[dayTimestamp].push(event); + } else { + eventsByDay[dayTimestamp] = [event]; + } + }); + + const todaysMidnight = this.timeUtils.getMidnightForTimestamp(); + this.filteredEvents = []; + Object.keys(eventsByDay).forEach((key) => { + const dayTimestamp = parseInt(key); + this.filteredEvents.push({ + color: dayTimestamp < todaysMidnight ? 'danger' : 'light', + dayTimestamp: dayTimestamp, + events: eventsByDay[dayTimestamp] + }); + }); + } else { + this.empty = true; + } } } @@ -69,8 +94,8 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { * @return {any[]} Filtered events. */ protected filterEventsByTime(start: number, end?: number): any[] { - start = moment().add(start, 'days').unix(); - end = typeof end != 'undefined' ? moment().add(end, 'days').unix() : end; + start = moment().add(start, 'days').startOf('day').unix(); + end = typeof end != 'undefined' ? moment().add(end, 'days').startOf('day').unix() : end; return this.events.filter((event) => { if (end) { @@ -85,20 +110,6 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { }); } - /** - * Update the events displayed. - */ - protected updateEvents(): void { - this.empty = !this.events || this.events.length <= 0; - if (!this.empty) { - this.recentlyOverdue = this.filterEventsByTime(-14, 0); - this.today = this.filterEventsByTime(0, 1); - this.next7Days = this.filterEventsByTime(1, 7); - this.next30Days = this.filterEventsByTime(7, 30); - this.future = this.filterEventsByTime(30); - } - } - /** * Load more events clicked. */ @@ -110,15 +121,20 @@ export class AddonBlockTimelineEventsComponent implements OnChanges { /** * Action clicked. * - * @param {Event} e Click event. - * @param {string} url Url of the action. + * @param {Event} e Click event. + * @param {any} event Calendar event info. */ - action(e: Event, url: string): void { + action(e: Event, event: any): void { e.preventDefault(); e.stopPropagation(); + let url; - // Fix URL format. - url = this.textUtils.decodeHTMLEntities(url); + if (event.action.actionable) { + // Fix URL format. + url = this.textUtils.decodeHTMLEntities(event.action.url); + } else { + url = this.textUtils.decodeHTMLEntities(event.url); + } const modal = this.domUtils.showModalLoading(); this.contentLinksHelper.handleLink(url, undefined, this.navCtrl).then((treated) => { diff --git a/src/addon/block/timeline/components/timeline/addon-block-timeline.html b/src/addon/block/timeline/components/timeline/addon-block-timeline.html index fa5b9fc0c..037e8b7a3 100644 --- a/src/addon/block/timeline/components/timeline/addon-block-timeline.html +++ b/src/addon/block/timeline/components/timeline/addon-block-timeline.html @@ -1,18 +1,31 @@ -
- - {{ 'addon.block_timeline.sortbydates' | translate }} - {{ 'addon.block_timeline.sortbycourses' | translate }} - +
+ + + {{ 'core.all' | translate }} + {{ 'addon.block_timeline.overdue' | translate }} + {{ 'addon.block_timeline.duedate' | translate }} + {{ 'addon.block_timeline.next7days' | translate }} + {{ 'addon.block_timeline.next30days' | translate }} + {{ 'addon.block_timeline.next3months' | translate }} + {{ 'addon.block_timeline.next6months' | translate }} + + + + + {{ 'addon.block_timeline.sortbydates' | translate }} + {{ 'addon.block_timeline.sortbycourses' | translate }} + +
- + - + diff --git a/src/addon/block/timeline/components/timeline/timeline.ts b/src/addon/block/timeline/components/timeline/timeline.ts index 1d7b839b9..180aec2ef 100644 --- a/src/addon/block/timeline/components/timeline/timeline.ts +++ b/src/addon/block/timeline/components/timeline/timeline.ts @@ -30,6 +30,7 @@ import { AddonBlockTimelineProvider } from '../../providers/timeline'; }) export class AddonBlockTimelineComponent extends AddonBlockComponent implements OnInit { sort = 'sortbydates'; + filter = 'next30days'; timeline = { events: [], loaded: false, @@ -40,6 +41,8 @@ export class AddonBlockTimelineComponent extends AddonBlockComponent implements loaded: false, canLoadMore: false }; + dataFrom: number; + dataTo: number; protected courseIds = []; protected fetchContentDefaultError = 'Error getting timeline data.'; @@ -55,6 +58,7 @@ export class AddonBlockTimelineComponent extends AddonBlockComponent implements * Component being initialized. */ ngOnInit(): void { + this.switchFilter(); super.ngOnInit(); } @@ -159,6 +163,39 @@ export class AddonBlockTimelineComponent extends AddonBlockComponent implements }); } + /** + * Change timeline filter being viewed. + */ + switchFilter(): void { + switch (this.filter) { + case 'overdue': + this.dataFrom = -14; + this.dataTo = 0; + break; + case 'next7days': + this.dataFrom = 0; + this.dataTo = 7; + break; + case 'next30days': + this.dataFrom = 0; + this.dataTo = 30; + break; + case 'next3months': + this.dataFrom = 0; + this.dataTo = 90; + break; + case 'next6months': + this.dataFrom = 0; + this.dataTo = 180; + break; + default: + case 'all': + this.dataFrom = -14; + this.dataTo = undefined; + break; + } + } + /** * Change timeline sort being viewed. */ diff --git a/src/addon/block/timeline/lang/en.json b/src/addon/block/timeline/lang/en.json index 86041e79f..1086d152e 100644 --- a/src/addon/block/timeline/lang/en.json +++ b/src/addon/block/timeline/lang/en.json @@ -1,9 +1,12 @@ { + "duedate": "Due date", "next30days": "Next 30 days", + "next3months": "Next 3 months", + "next6months": "Next 6 months", "next7days": "Next 7 days", "nocoursesinprogress": "No in progress courses", "noevents": "No upcoming activities due", - "recentlyoverdue": "Recently overdue", + "overdue": "Overdue", "sortbycourses": "Sort by courses", "sortbydates": "Sort by dates" } \ No newline at end of file diff --git a/src/app/app.scss b/src/app/app.scss index 0813e34e9..f5bad6d6d 100644 --- a/src/app/app.scss +++ b/src/app/app.scss @@ -417,12 +417,7 @@ ion-app.app-root { margin: 0; } - // Ionic fix. Button can occupy all page if not. - ion-select { - position: relative; - } - - ion-col ion-select { + ion-col ion-select:not([text-start]) { @include float(end); max-width: none; width: 100%; @@ -432,7 +427,12 @@ ion-app.app-root { } } + .item-radio-disabled ion-radio[ng-reflect-value="disabled"]{ + display: none; + } + ion-select { + position: relative; // Ionic fix. Button can occupy all page if not. color: $core-select-placeholder-color; align-self: start; diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json index 9e2f21c71..3b9112a85 100644 --- a/src/assets/lang/en.json +++ b/src/assets/lang/en.json @@ -17,11 +17,14 @@ "addon.block_myoverview.nocoursesinprogress": "No in progress courses", "addon.block_myoverview.nocoursespast": "No past courses", "addon.block_myoverview.past": "Past", + "addon.block_timeline.duedate": "Due date", "addon.block_timeline.next30days": "Next 30 days", + "addon.block_timeline.next3months": "Next 3 months", + "addon.block_timeline.next6months": "Next 6 months", "addon.block_timeline.next7days": "Next 7 days", "addon.block_timeline.nocoursesinprogress": "No in progress courses", "addon.block_timeline.noevents": "No upcoming activities due", - "addon.block_timeline.recentlyoverdue": "Recently overdue", + "addon.block_timeline.overdue": "Overdue", "addon.block_timeline.sortbycourses": "Sort by courses", "addon.block_timeline.sortbydates": "Sort by dates", "addon.calendar.calendar": "Calendar", @@ -1058,6 +1061,7 @@ "core.accounts": "Accounts", "core.add": "Add", "core.agelocationverification": "Age and location verification", + "core.all": "All", "core.allparticipants": "All participants", "core.android": "Android", "core.answer": "Answer", @@ -1139,7 +1143,6 @@ "core.courses.cannotretrievemorecategories": "Categories deeper than level {{$a}} cannot be retrieved.", "core.courses.categories": "Course categories", "core.courses.confirmselfenrol": "Are you sure you want to enrol yourself in this course?", - "core.courses.courseoverview": "Course overview", "core.courses.courses": "Courses", "core.courses.downloadcourses": "Download courses", "core.courses.enrolme": "Enrol me", @@ -1150,6 +1153,7 @@ "core.courses.filtermycourses": "Filter my courses", "core.courses.frontpage": "Front page", "core.courses.mycourses": "My courses", + "core.courses.mymoodle": "Dashboard", "core.courses.nocourses": "No course information to show.", "core.courses.nocoursesyet": "No courses in this category", "core.courses.nosearchresults": "No results", diff --git a/src/components/empty-box/core-empty-box.html b/src/components/empty-box/core-empty-box.html index 6c23d5e9b..f131769f5 100644 --- a/src/components/empty-box/core-empty-box.html +++ b/src/components/empty-box/core-empty-box.html @@ -1,4 +1,4 @@ -
+
diff --git a/src/components/empty-box/empty-box.scss b/src/components/empty-box/empty-box.scss index 2dbb4f072..14b4e1b93 100644 --- a/src/components/empty-box/empty-box.scss +++ b/src/components/empty-box/empty-box.scss @@ -33,6 +33,7 @@ ion-app.app-root core-empty-box { img { height: 125px; width: 145px; + margin: 0 auto; } p { font-size: 120%; diff --git a/src/components/empty-box/empty-box.ts b/src/components/empty-box/empty-box.ts index a1b95fcc5..0d9fdd7ec 100644 --- a/src/components/empty-box/empty-box.ts +++ b/src/components/empty-box/empty-box.ts @@ -28,6 +28,8 @@ export class CoreEmptyBoxComponent { @Input() message: string; // Message to display. @Input() icon?: string; // Name of the icon to use. @Input() image?: string; // Image source. If an icon is provided, image won't be used. + @Input() inline?: boolean; // If this has to be shown inline instead of occupying whole page. + // If image or icon is not supplied, it's true by default. constructor() { // Nothing to do. diff --git a/src/components/tabs/core-tabs.html b/src/components/tabs/core-tabs.html index 4958a47b6..7b0e2954b 100644 --- a/src/components/tabs/core-tabs.html +++ b/src/components/tabs/core-tabs.html @@ -1,5 +1,5 @@ -
+
diff --git a/src/core/courses/components/course-progress/course-progress.scss b/src/core/courses/components/course-progress/course-progress.scss index 3546320be..241553bd5 100644 --- a/src/core/courses/components/course-progress/course-progress.scss +++ b/src/core/courses/components/course-progress/course-progress.scss @@ -48,6 +48,15 @@ ion-app.app-root core-courses-course-progress { .label { @include margin(0, 0, 0, null); } + ion-item-divider .label-md { + @extend .label-md; + } + ion-item-divider .label-wp { + @extend .label-wp; + } + ion-item-divider .label-ios { + @extend .label-ios; + } } button { diff --git a/src/core/courses/courses.module.ts b/src/core/courses/courses.module.ts index 8920a72fe..81f632e9f 100644 --- a/src/core/courses/courses.module.ts +++ b/src/core/courses/courses.module.ts @@ -15,7 +15,7 @@ import { NgModule } from '@angular/core'; import { CoreCoursesProvider } from './providers/courses'; import { CoreCoursesHelperProvider } from './providers/helper'; -import { CoreCoursesMainMenuHandler } from './providers/mainmenu-handler'; +import { CoreDashboardMainMenuHandler } from './providers/mainmenu-handler'; import { CoreCoursesDashboardProvider } from './providers/dashboard'; import { CoreCoursesCourseLinkHandler } from './providers/course-link-handler'; import { CoreCoursesIndexLinkHandler } from './providers/courses-index-link-handler'; @@ -38,7 +38,7 @@ export const CORE_COURSES_PROVIDERS: any[] = [ CoreCoursesProvider, CoreCoursesDashboardProvider, CoreCoursesHelperProvider, - CoreCoursesMainMenuHandler, + CoreDashboardMainMenuHandler, CoreCoursesCourseLinkHandler, CoreCoursesIndexLinkHandler, CoreCoursesDashboardLinkHandler @@ -47,7 +47,7 @@ export const CORE_COURSES_PROVIDERS: any[] = [ }) export class CoreCoursesModule { constructor(mainMenuDelegate: CoreMainMenuDelegate, contentLinksDelegate: CoreContentLinksDelegate, - mainMenuHandler: CoreCoursesMainMenuHandler, courseLinkHandler: CoreCoursesCourseLinkHandler, + mainMenuHandler: CoreDashboardMainMenuHandler, courseLinkHandler: CoreCoursesCourseLinkHandler, indexLinkHandler: CoreCoursesIndexLinkHandler, dashboardLinkHandler: CoreCoursesDashboardLinkHandler) { mainMenuDelegate.registerHandler(mainMenuHandler); diff --git a/src/core/courses/lang/en.json b/src/core/courses/lang/en.json index 4bffa7112..a372d5c29 100644 --- a/src/core/courses/lang/en.json +++ b/src/core/courses/lang/en.json @@ -4,8 +4,8 @@ "cannotretrievemorecategories": "Categories deeper than level {{$a}} cannot be retrieved.", "categories": "Course categories", "confirmselfenrol": "Are you sure you want to enrol yourself in this course?", - "courseoverview": "Course overview", "courses": "Courses", + "mymoodle": "Dashboard", "downloadcourses": "Download courses", "enrolme": "Enrol me", "errorloadcategories": "An error occurred while loading categories.", diff --git a/src/core/courses/pages/dashboard/dashboard.html b/src/core/courses/pages/dashboard/dashboard.html index 482a53c9b..c152e5b29 100644 --- a/src/core/courses/pages/dashboard/dashboard.html +++ b/src/core/courses/pages/dashboard/dashboard.html @@ -24,7 +24,7 @@ - + @@ -36,7 +36,7 @@ - + diff --git a/src/core/courses/pages/dashboard/dashboard.ts b/src/core/courses/pages/dashboard/dashboard.ts index 36a2ca410..2bffccbb2 100644 --- a/src/core/courses/pages/dashboard/dashboard.ts +++ b/src/core/courses/pages/dashboard/dashboard.ts @@ -22,6 +22,7 @@ import { AddonBlockMyOverviewComponent } from '@addon/block/myoverview/component import { AddonBlockTimelineComponent } from '@addon/block/timeline/components/timeline/timeline'; import { CoreTabsComponent } from '@components/tabs/tabs'; import { CoreSiteHomeIndexComponent } from '@core/sitehome/components/index/index'; +import { AddonBlockTimelineProvider } from '@addon/block/timeline/providers/timeline'; /** * Page that displays the dashboard. @@ -38,7 +39,9 @@ export class CoreCoursesDashboardPage implements OnDestroy { @ViewChild(AddonBlockTimelineComponent) blockTimeline: AddonBlockTimelineComponent; firstSelectedTab: number; - siteHomeEnabled: boolean; + siteHomeEnabled = false; + timelineEnabled = false; + coursesEnabled = false; tabsReady = false; tabShown = 'courses'; searchEnabled: boolean; @@ -51,7 +54,7 @@ export class CoreCoursesDashboardPage implements OnDestroy { constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider, private sitesProvider: CoreSitesProvider, private siteHomeProvider: CoreSiteHomeProvider, - private eventsProvider: CoreEventsProvider) { + private eventsProvider: CoreEventsProvider, private timelineProvider: AddonBlockTimelineProvider) { this.loadSiteName(); } @@ -67,13 +70,29 @@ export class CoreCoursesDashboardPage implements OnDestroy { this.loadSiteName(); }); - // Decide which tab to load first. - this.siteHomeProvider.isAvailable().then((enabled) => { - const site = this.sitesProvider.getCurrentSite(), - displaySiteHome = site.getInfo() && site.getInfo().userhomepage === 0; + const promises = []; + // Decide which tab to load first. + promises.push(this.siteHomeProvider.isAvailable().then((enabled) => { this.siteHomeEnabled = enabled; - this.firstSelectedTab = displaySiteHome ? 0 : 1; + })); + + promises.push(this.timelineProvider.isAvailable().then((enabled) => { + this.timelineEnabled = enabled; + })); + + this.coursesEnabled = !this.coursesProvider.isMyCoursesDisabledInSite(); + + Promise.all(promises).finally(() => { + if (this.siteHomeEnabled && (this.coursesEnabled || this.timelineEnabled)) { + const site = this.sitesProvider.getCurrentSite(), + displaySiteHome = site.getInfo() && site.getInfo().userhomepage === 0; + + this.firstSelectedTab = displaySiteHome ? 0 : 1; + } else { + this.firstSelectedTab = this.siteHomeEnabled ? 1 : 0; + } + this.tabsReady = true; }); } diff --git a/src/core/courses/providers/dashboard.ts b/src/core/courses/providers/dashboard.ts index fbc56d3cc..0cc2b8b10 100644 --- a/src/core/courses/providers/dashboard.ts +++ b/src/core/courses/providers/dashboard.ts @@ -13,8 +13,7 @@ // limitations under the License. import { Injectable } from '@angular/core'; -import { CoreSitesProvider } from '@providers/sites'; -import { CoreSite } from '@classes/site'; +import { CoreCoursesProvider } from './courses'; import { AddonBlockTimelineProvider } from '@addon/block/timeline/providers/timeline'; /** @@ -23,7 +22,7 @@ import { AddonBlockTimelineProvider } from '@addon/block/timeline/providers/time @Injectable() export class CoreCoursesDashboardProvider { - constructor(private sitesProvider: CoreSitesProvider, private timelineProvider: AddonBlockTimelineProvider) { } + constructor(private coursesProvider: CoreCoursesProvider, private timelineProvider: AddonBlockTimelineProvider) { } /** * Returns whether or not My Overview is available for a certain site. @@ -35,25 +34,13 @@ export class CoreCoursesDashboardProvider { return this.timelineProvider.isAvailable(siteId); } - /** - * Check if My Overview is disabled in a certain site. - * - * @param {CoreSite} [site] Site. If not defined, use current site. - * @return {boolean} Whether it's disabled. - */ - isDisabledInSite(site?: CoreSite): boolean { - site = site || this.sitesProvider.getCurrentSite(); - - return site.isFeatureDisabled('CoreMainMenuDelegate_CoreCourses'); - } - /** * Check if My Overview is available and not disabled. * * @return {Promise} Promise resolved with true if enabled, resolved with false otherwise. */ isEnabled(): Promise { - if (!this.isDisabledInSite()) { + if (!this.coursesProvider.isMyCoursesDisabledInSite()) { return this.isAvailable().catch(() => { return false; }); diff --git a/src/core/courses/providers/mainmenu-handler.ts b/src/core/courses/providers/mainmenu-handler.ts index 172cb7378..95036e22a 100644 --- a/src/core/courses/providers/mainmenu-handler.ts +++ b/src/core/courses/providers/mainmenu-handler.ts @@ -16,17 +16,18 @@ import { Injectable } from '@angular/core'; import { CoreCoursesProvider } from './courses'; import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; import { CoreCoursesDashboardProvider } from '../providers/dashboard'; +import { CoreSiteHomeProvider } from '@core/sitehome/providers/sitehome'; /** - * Handler to add My Courses or My Overview into main menu. + * Handler to add Dashboard into main menu. */ @Injectable() -export class CoreCoursesMainMenuHandler implements CoreMainMenuHandler { - name = 'CoreCourses'; +export class CoreDashboardMainMenuHandler implements CoreMainMenuHandler { + name = 'CoreDashboard'; // Old name CoreCourses cannot be used because it would be all disabled by site. priority = 1100; - isDashboardEnabled: boolean; - constructor(private coursesProvider: CoreCoursesProvider, private dashboardProvider: CoreCoursesDashboardProvider) { } + constructor(private coursesProvider: CoreCoursesProvider, private dashboardProvider: CoreCoursesDashboardProvider, + private siteHomeProvider: CoreSiteHomeProvider) { } /** * Check if the handler is enabled on a site level. @@ -36,13 +37,19 @@ export class CoreCoursesMainMenuHandler implements CoreMainMenuHandler { isEnabled(): boolean | Promise { // Check if my overview is enabled. return this.dashboardProvider.isEnabled().then((enabled) => { - this.isDashboardEnabled = enabled; if (enabled) { return true; } - // My overview not enabled, check if my courses is enabled. - return !this.coursesProvider.isMyCoursesDisabledInSite(); + return this.siteHomeProvider.isAvailable().then((enabled) => { + // Show in case siteHome is enabled. + if (enabled) { + return true; + } + + // My overview not enabled, check if my courses is enabled. + return !this.coursesProvider.isMyCoursesDisabledInSite(); + }); }); } @@ -52,20 +59,11 @@ export class CoreCoursesMainMenuHandler implements CoreMainMenuHandler { * @return {CoreMainMenuHandlerData} Data needed to render the handler. */ getDisplayData(): CoreMainMenuHandlerData { - if (this.isDashboardEnabled) { - return { - icon: 'home', - title: 'core.courses.courseoverview', - page: 'CoreCoursesDashboardPage', - class: 'core-courseoverview-handler' - }; - } else { - return { - icon: 'fa-graduation-cap', - title: 'core.courses.mycourses', - page: 'CoreCoursesMyCoursesPage', - class: 'core-mycourses-handler' - }; - } + return { + icon: 'home', + title: 'core.courses.mymoodle', + page: 'CoreCoursesDashboardPage', + class: 'core-dashboard-handler' + }; } } diff --git a/src/core/mainmenu/pages/menu/menu.ts b/src/core/mainmenu/pages/menu/menu.ts index 89567ab8d..a9fc4de25 100644 --- a/src/core/mainmenu/pages/menu/menu.ts +++ b/src/core/mainmenu/pages/menu/menu.ts @@ -55,9 +55,6 @@ export class CoreMainMenuPage implements OnDestroy { this.showTabs = true; - const site = this.sitesProvider.getCurrentSite(), - displaySiteHome = site.getInfo() && site.getInfo().userhomepage === 0; - this.subscription = this.menuDelegate.getHandlers().subscribe((handlers) => { handlers = handlers.slice(0, CoreMainMenuProvider.NUM_MAIN_HANDLERS); // Get main handlers. @@ -83,31 +80,28 @@ export class CoreMainMenuPage implements OnDestroy { }); if (typeof this.initialTab == 'undefined' && !this.loaded) { + this.initialTab = 0; + // Calculate the tab to load. if (this.redirectPage) { // Check if the redirect page is the root page of any of the tabs. - this.initialTab = 0; - - for (let i = 0; i < this.tabs.length; i++) { - const tab = this.tabs[i]; - if (tab.page == this.redirectPage) { - // Tab found. Set the params and unset the redirect page. - this.initialTab = i + 1; - tab.pageParams = Object.assign(tab.pageParams || {}, this.redirectParams); - this.redirectPage = null; - this.redirectParams = null; - break; - } + const i = this.tabs.findIndex((tab, i) => { + return tab.page == this.redirectPage; + }); + if (i >= 0) { + // Tab found. Set the params and unset the redirect page. + this.initialTab = i + 1; + this.tabs[i].pageParams = Object.assign(this.tabs[i].pageParams || {}, this.redirectParams); + this.redirectPage = null; + this.redirectParams = null; } } else { - // By default, course overview will be loaded (3.3+). Check if we need to select Site Home or My Courses. - for (let i = 0; i < this.tabs.length; i++) { - const handler = handlers[i]; - if ((displaySiteHome && handler.name == 'CoreSiteHome') || - (!displaySiteHome && handler.name == 'CoreCourses')) { - this.initialTab = i; - break; - } + const i = handlers.findIndex((handler, i) => { + return handler.name == 'CoreDashboard'; + }); + + if (i >= 0) { + this.initialTab = i; } } } diff --git a/src/core/sitehome/components/index/index.ts b/src/core/sitehome/components/index/index.ts index df8452399..ee581ec75 100644 --- a/src/core/sitehome/components/index/index.ts +++ b/src/core/sitehome/components/index/index.ts @@ -28,7 +28,7 @@ import { CoreSiteHomeProvider } from '../../providers/sitehome'; templateUrl: 'core-sitehome-index.html', }) export class CoreSiteHomeIndexComponent implements OnInit { - dataLoaded: boolean; + dataLoaded = false; section: any; block: any; hasContent: boolean; diff --git a/src/core/sitehome/providers/mainmenu-handler.ts b/src/core/sitehome/providers/mainmenu-handler.ts deleted file mode 100644 index 6c81774ed..000000000 --- a/src/core/sitehome/providers/mainmenu-handler.ts +++ /dev/null @@ -1,61 +0,0 @@ -// (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 { Injectable } from '@angular/core'; -import { CoreSiteHomeProvider } from './sitehome'; -import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate'; -import { CoreCoursesDashboardProvider } from '@core/courses/providers/dashboard'; - -/** - * Handler to add Site Home into main menu. - */ -@Injectable() -export class CoreSiteHomeMainMenuHandler implements CoreMainMenuHandler { - name = 'CoreSiteHome'; - priority = 1200; - - constructor(private siteHomeProvider: CoreSiteHomeProvider, private dashboardProvider: CoreCoursesDashboardProvider) { } - - /** - * Check if the handler is enabled on a site level. - * - * @return {boolean} Whether or not the handler is enabled on a site level. - */ - isEnabled(): boolean | Promise { - // Check if my overview is enabled. - return this.dashboardProvider.isEnabled().then((enabled) => { - if (enabled) { - // My overview is enabled, Site Home will be inside the overview page. - return false; - } - - // My overview not enabled, check if site home is enabled. - return this.siteHomeProvider.isAvailable(); - }); - } - - /** - * Returns the data needed to render the handler. - * - * @return {CoreMainMenuHandlerData} Data needed to render the handler. - */ - getDisplayData(): CoreMainMenuHandlerData { - return { - icon: 'home', - title: 'core.sitehome.sitehome', - page: 'CoreSiteHomeIndexPage', - class: 'core-sitehome-handler' - }; - } -} diff --git a/src/core/sitehome/sitehome.module.ts b/src/core/sitehome/sitehome.module.ts index 77ca68377..60491cc58 100644 --- a/src/core/sitehome/sitehome.module.ts +++ b/src/core/sitehome/sitehome.module.ts @@ -14,9 +14,7 @@ import { NgModule } from '@angular/core'; import { CoreSiteHomeProvider } from './providers/sitehome'; -import { CoreSiteHomeMainMenuHandler } from './providers/mainmenu-handler'; import { CoreSiteHomeIndexLinkHandler } from './providers/index-link-handler'; -import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate'; import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate'; // List of providers (without handlers). @@ -30,15 +28,12 @@ export const CORE_SITEHOME_PROVIDERS: any[] = [ ], providers: [ CoreSiteHomeProvider, - CoreSiteHomeMainMenuHandler, CoreSiteHomeIndexLinkHandler ], exports: [] }) export class CoreSiteHomeModule { - constructor(mainMenuDelegate: CoreMainMenuDelegate, contentLinksDelegate: CoreContentLinksDelegate, - mainMenuHandler: CoreSiteHomeMainMenuHandler, indexLinkHandler: CoreSiteHomeIndexLinkHandler) { - mainMenuDelegate.registerHandler(mainMenuHandler); + constructor(contentLinksDelegate: CoreContentLinksDelegate, indexLinkHandler: CoreSiteHomeIndexLinkHandler) { contentLinksDelegate.registerHandler(indexLinkHandler); } } diff --git a/src/lang/en.json b/src/lang/en.json index c439bd395..ebd2b9aa2 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -1,6 +1,7 @@ { "accounts": "Accounts", "add": "Add", + "all": "All", "agelocationverification": "Age and location verification", "allparticipants": "All participants", "android": "Android", diff --git a/src/providers/utils/time.ts b/src/providers/utils/time.ts index 8ab41f1f9..e1fb8aca1 100644 --- a/src/providers/utils/time.ts +++ b/src/providers/utils/time.ts @@ -163,4 +163,21 @@ export class CoreTimeUtilsProvider { getLocalizedDateFormat(localizedFormat: any): string { return moment.localeData().longDateFormat(localizedFormat); } + + /** + * For a given timestamp get the midnight value in the user's timezone. + * + * The calculation is performed relative to the user's midnight timestamp + * for today to ensure that timezones are preserved. + * + * @param {number} [timestamp] The timestamp to calculate from. If not defined, return today's midnight. + * @return {number} The midnight value of the user's timestamp. + */ + getMidnightForTimestamp(timestamp?: number): number { + if (timestamp) { + return moment(timestamp * 1000).startOf('day').unix(); + } else { + return moment().startOf('day').unix(); + } + } }