commit
80503d8b08
|
@ -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",
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
<core-loading [hideUntil]="loaded" class="core-loading-center">
|
||||
<!-- "Time" selector. -->
|
||||
<div padding class="clearfix" [hidden]="showFilter" ion-row justify-content-between>
|
||||
<ion-select float-start [title]="'core.show' | translate" [(ngModel)]="selectedFilter" ion-col (ngModelChange)="selectedChanged()" interface="popover" class="core-button-select">
|
||||
<div padding class="clearfix" [hidden]="showFilter" ion-row justify-content-end>
|
||||
<ion-select [hidden]="!showSelectorFilter" [title]="'core.show' | translate" [(ngModel)]="selectedFilter" ion-col (ngModelChange)="selectedChanged()" interface="popover" class="core-button-select">
|
||||
<ion-option value="inprogress">{{ 'addon.block_myoverview.inprogress' | translate }}</ion-option>
|
||||
<ion-option value="future">{{ 'addon.block_myoverview.future' | translate }}</ion-option>
|
||||
<ion-option value="past">{{ 'addon.block_myoverview.past' | translate }}</ion-option>
|
||||
</ion-select>
|
||||
<!-- Download all courses. -->
|
||||
<div *ngIf="downloadAllCoursesEnabled && courses[selectedFilter] && courses[selectedFilter].length > 1" class="core-button-spinner">
|
||||
<div *ngIf="downloadAllCoursesEnabled && courses[selectedFilter] && courses[selectedFilter].length > 1" class="core-button-spinner" ion-col text-end col-1>
|
||||
<button *ngIf="prefetchCoursesData[selectedFilter].icon && prefetchCoursesData[selectedFilter].icon != 'spinner'" ion-button icon-only clear color="dark" (click)="prefetchCourses()">
|
||||
<core-icon [name]="prefetchCoursesData[selectedFilter].icon"></core-icon>
|
||||
</button>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -1,49 +1,25 @@
|
|||
<ng-template #eventTemplate let-event="event">
|
||||
<a ion-item core-link text-wrap detail-none capture="true" class="core-course-module-handler item-media" [href]="event.url" [title]="event.name">
|
||||
<img item-start [src]="event.iconUrl" core-external-content alt="" role="presentation" *ngIf="event.iconUrl" class="core-module-icon">
|
||||
<h2><core-format-text [text]="event.name"></core-format-text></h2>
|
||||
<p>{{event.timesort * 1000 | coreFormatDate:"dfmediumdate" }} <core-format-text *ngIf="showCourse" [text]="event.course.fullnamedisplay"></core-format-text></p>
|
||||
<button ion-button clear item-end class="hidden-phone" (click)="action($event, event.action.url)" [title]="event.action.name" [disabled]="!event.action.actionable" *ngIf="event.action">
|
||||
{{event.action.name}}
|
||||
<ion-badge item-end margin-start *ngIf="event.action.showitemcount">{{event.action.itemcount}}</ion-badge>
|
||||
</button>
|
||||
<ion-badge class="hidden-tablet" item-end *ngIf="event.action.showitemcount">{{event.action.itemcount}}</ion-badge>
|
||||
</a>
|
||||
</ng-template>
|
||||
|
||||
<ion-item-group *ngIf="recentlyOverdue.length > 0">
|
||||
<ion-item-divider color="danger">{{ 'addon.block_timeline.recentlyoverdue' | translate }}</ion-item-divider>
|
||||
<ng-container *ngFor="let event of recentlyOverdue">
|
||||
<ng-container *ngTemplateOutlet="eventTemplate; context: {event: event}"></ng-container>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
<ion-item-group *ngIf="next7Days.length > 0">
|
||||
<ion-item-divider color="light">{{ 'addon.block_timeline.next7days' | translate }}</ion-item-divider>
|
||||
<ng-container *ngFor="let event of next7Days">
|
||||
<ng-container *ngTemplateOutlet="eventTemplate; context: {event: event}"></ng-container>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
<ion-item-group *ngIf="next30Days.length > 0">
|
||||
<ion-item-divider color="light">{{ 'addon.block_timeline.next30days' | translate }}</ion-item-divider>
|
||||
<ng-container *ngFor="let event of next30Days">
|
||||
<ng-container *ngTemplateOutlet="eventTemplate; context: {event: event}"></ng-container>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
<ion-item-group *ngIf="future.length > 0">
|
||||
<ion-item-divider color="light">{{ 'addon.block_myoverview.future' | translate }}</ion-item-divider>
|
||||
<ng-container *ngFor="let event of future">
|
||||
<ng-container *ngTemplateOutlet="eventTemplate; context: {event: event}"></ng-container>
|
||||
<ion-item-group *ngFor="let dayEvents of filteredEvents">
|
||||
<ion-item-divider [color]="dayEvents.color">
|
||||
<h2>{{ dayEvents.dayTimestamp * 1000 | coreFormatDate:"LL" }}</h2>
|
||||
</ion-item-divider>
|
||||
<ng-container *ngFor="let event of dayEvents.events">
|
||||
<a ion-item text-wrap detail-none class="core-course-module-handler item-media" (click)="action($event, event)" [title]="event.action.actionable ? event.action.name: event.name">
|
||||
<img item-start [src]="event.iconUrl" core-external-content alt="" role="presentation" *ngIf="event.iconUrl" class="core-module-icon">
|
||||
<h2><core-format-text [text]="event.name"></core-format-text></h2>
|
||||
<p *ngIf="showCourse">
|
||||
<core-format-text [text]="event.course.fullnamedisplay"></core-format-text>
|
||||
</p>
|
||||
<ion-badge color="light" item-end>{{event.timesort * 1000 | coreFormatDate:"LT" }}</ion-badge>
|
||||
</a>
|
||||
</ng-container>
|
||||
</ion-item-group>
|
||||
|
||||
<div padding text-center *ngIf="canLoadMore && !empty">
|
||||
<!-- Button and spinner to show more attempts. -->
|
||||
<button *ngIf="!loadingMore" ion-button block (click)="loadMoreEvents()">{{ 'core.loadmore' | translate }}</button>
|
||||
<button ion-button block (click)="loadMoreEvents()" color="light" *ngIf="!loadingMore">
|
||||
{{ 'core.loadmore' | translate }}
|
||||
</button>
|
||||
<ion-spinner *ngIf="loadingMore"></ion-spinner>
|
||||
</div>
|
||||
|
||||
<core-empty-box *ngIf="empty && showCourse" image="assets/img/icons/activities.svg" [message]="'addon.block_timeline.noevents' | translate"></core-empty-box>
|
||||
<core-empty-box *ngIf="empty && !showCourse" [message]="'addon.block_timeline.noevents' | translate"></core-empty-box>
|
||||
<core-empty-box *ngIf="empty" image="assets/img/icons/activities.svg" [message]="'addon.block_timeline.noevents' | translate" [inline]="!showCourse"></core-empty-box>
|
||||
|
|
|
@ -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<void>; // 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) => {
|
||||
|
|
|
@ -1,18 +1,31 @@
|
|||
<div padding [hidden]="!loaded">
|
||||
<ion-select [(ngModel)]="sort" (ngModelChange)="switchSort()" interface="popover" class="core-button-select">
|
||||
<ion-option value="sortbydates">{{ 'addon.block_timeline.sortbydates' | translate }}</ion-option>
|
||||
<ion-option value="sortbycourses">{{ 'addon.block_timeline.sortbycourses' | translate }}</ion-option>
|
||||
</ion-select>
|
||||
<div padding [hidden]="!loaded" ion-row>
|
||||
<ion-col>
|
||||
<ion-select text-start [(ngModel)]="filter" (ngModelChange)="switchFilter()" interface="popover" class="core-button-select">
|
||||
<ion-option value="all">{{ 'core.all' | translate }}</ion-option>
|
||||
<ion-option value="overdue">{{ 'addon.block_timeline.overdue' | translate }}</ion-option>
|
||||
<ion-option disabled value="disabled">{{ 'addon.block_timeline.duedate' | translate }}</ion-option>
|
||||
<ion-option value="next7days">{{ 'addon.block_timeline.next7days' | translate }}</ion-option>
|
||||
<ion-option value="next30days">{{ 'addon.block_timeline.next30days' | translate }}</ion-option>
|
||||
<ion-option value="next3months">{{ 'addon.block_timeline.next3months' | translate }}</ion-option>
|
||||
<ion-option value="next6months">{{ 'addon.block_timeline.next6months' | translate }}</ion-option>
|
||||
</ion-select>
|
||||
</ion-col>
|
||||
<ion-col>
|
||||
<ion-select text-start [(ngModel)]="sort" (ngModelChange)="switchSort()" interface="popover" class="core-button-select">
|
||||
<ion-option value="sortbydates">{{ 'addon.block_timeline.sortbydates' | translate }}</ion-option>
|
||||
<ion-option value="sortbycourses">{{ 'addon.block_timeline.sortbycourses' | translate }}</ion-option>
|
||||
</ion-select>
|
||||
</ion-col>
|
||||
</div>
|
||||
<core-loading [hideUntil]="loaded && timeline.loaded" [hidden]="sort != 'sortbydates'" class="core-loading-center">
|
||||
<addon-block-timeline-events [events]="timeline.events" showCourse="true" [canLoadMore]="timeline.canLoadMore" (loadMore)="loadMoreTimeline()"></addon-block-timeline-events>
|
||||
<addon-block-timeline-events [events]="timeline.events" showCourse="true" [canLoadMore]="timeline.canLoadMore" (loadMore)="loadMoreTimeline()" [from]="dataFrom" [to]="dataTo"></addon-block-timeline-events>
|
||||
</core-loading>
|
||||
<core-loading [hideUntil]="loaded && timelineCourses.loaded" [hidden]="sort != 'sortbycourses'" class="core-loading-center">
|
||||
<ion-grid no-padding>
|
||||
<ion-row no-padding>
|
||||
<ion-col *ngFor="let course of timelineCourses.courses" no-padding col-12 col-md-6>
|
||||
<core-courses-course-progress [course]="course">
|
||||
<addon-block-timeline-events [events]="course.events" [canLoadMore]="course.canLoadMore" (loadMore)="loadMoreCourse(course)"></addon-block-timeline-events>
|
||||
<addon-block-timeline-events [events]="course.events" [canLoadMore]="course.canLoadMore" (loadMore)="loadMoreCourse(course)" [from]="dataFrom" [to]="dataTo"></addon-block-timeline-events>
|
||||
</core-courses-course-progress>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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"
|
||||
}
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<div class="core-empty-box" [class.core-empty-box-inline]="!image && !icon">
|
||||
<div class="core-empty-box" [class.core-empty-box-inline]="(!image && !icon) || inline">
|
||||
<div class="core-empty-box-content" padding>
|
||||
<img *ngIf="image && !icon" [src]="image" role="presentation">
|
||||
<core-icon *ngIf="icon" [name]="icon"></core-icon>
|
||||
|
|
|
@ -33,6 +33,7 @@ ion-app.app-root core-empty-box {
|
|||
img {
|
||||
height: 125px;
|
||||
width: 145px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
p {
|
||||
font-size: 120%;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<core-loading [hideUntil]="hideUntil" class="core-loading-center">
|
||||
<div class="core-tabs-bar" #topTabs [hidden]="!tabs || tabs.length < 2">
|
||||
<div class="core-tabs-bar" #topTabs [hidden]="!tabs || numTabsShown < 2">
|
||||
<ion-row>
|
||||
<ion-col class="col-with-arrow" (click)="slidePrev()" no-padding col-1>
|
||||
<ion-icon *ngIf="showPrevButton" name="arrow-back" md="ios-arrow-back"></ion-icon>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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.",
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
</core-tab>
|
||||
|
||||
<!-- Courses tab. -->
|
||||
<core-tab [title]="'core.courses.courses' | translate" (ionSelect)="tabChanged('courses')">
|
||||
<core-tab [show]="coursesEnabled" [title]="'core.courses.courses' | translate" (ionSelect)="tabChanged('courses')">
|
||||
<ng-template>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="!!blockMyOverview && blockMyOverview.loaded" (ionRefresh)="blockMyOverview.doRefresh($event)">
|
||||
|
@ -36,7 +36,7 @@
|
|||
</core-tab>
|
||||
|
||||
<!-- Timeline tab. -->
|
||||
<core-tab [title]="'core.courses.timeline' | translate" (ionSelect)="tabChanged('timeline')">
|
||||
<core-tab [show]="timelineEnabled" [title]="'core.courses.timeline' | translate" (ionSelect)="tabChanged('timeline')">
|
||||
<ng-template>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="!!blockTimeline && blockTimeline.loaded" (ionRefresh)="blockTimeline.doRefresh($event)">
|
||||
|
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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<boolean>} Promise resolved with true if enabled, resolved with false otherwise.
|
||||
*/
|
||||
isEnabled(): Promise<boolean> {
|
||||
if (!this.isDisabledInSite()) {
|
||||
if (!this.coursesProvider.isMyCoursesDisabledInSite()) {
|
||||
return this.isAvailable().catch(() => {
|
||||
return false;
|
||||
});
|
||||
|
|
|
@ -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<boolean> {
|
||||
// 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'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<boolean> {
|
||||
// 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'
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"accounts": "Accounts",
|
||||
"add": "Add",
|
||||
"all": "All",
|
||||
"agelocationverification": "Age and location verification",
|
||||
"allparticipants": "All participants",
|
||||
"android": "Android",
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue