commit
4c292da21d
|
@ -7,7 +7,7 @@
|
|||
<ion-refresher [enabled]="eventLoaded" (ionRefresh)="refreshEvent($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="eventLoaded" class="core-loading-center">
|
||||
<core-loading [hideUntil]="eventLoaded">
|
||||
<ion-card>
|
||||
<ion-card-content>
|
||||
<ion-card-title text-wrap>
|
||||
|
@ -40,7 +40,7 @@
|
|||
<ion-card list *ngIf="notificationsEnabled">
|
||||
<ion-item>
|
||||
<ion-label>{{ 'addon.calendar.notifications' | translate }}</ion-label>
|
||||
<ion-select [(ngModel)]="notificationTime" (ionChange)="updateNotificationTime($event)">
|
||||
<ion-select [(ngModel)]="notificationTime" (ionChange)="updateNotificationTime($event)" interface="popover">
|
||||
<ion-option value="-1">{{ 'core.defaultvalue' | translate :{$a: defaultTimeReadable} }}</ion-option>
|
||||
<ion-option value="0">{{ 'core.settings.disabled' | translate }}</ion-option>
|
||||
<ion-option value="10">{{ 600 | coreDuration }}</ion-option>
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
<ion-refresher [enabled]="eventsLoaded" (ionRefresh)="refreshEvents($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
<core-loading [hideUntil]="eventsLoaded" class="core-loading-center">
|
||||
<core-loading [hideUntil]="eventsLoaded">
|
||||
<core-empty-box *ngIf="!filteredEvents || !filteredEvents.length" icon="calendar" [message]="'addon.calendar.noevents' | translate">
|
||||
</core-empty-box>
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<ion-list>
|
||||
<ion-item>
|
||||
<ion-label>{{ 'addon.calendar.defaultnotificationtime' | translate }}</ion-label>
|
||||
<ion-select [(ngModel)]="defaultTime" (ionChange)="updateDefaultTime($event)">
|
||||
<ion-select [(ngModel)]="defaultTime" (ionChange)="updateDefaultTime($event)" interface="popover">
|
||||
<ion-option value="0">{{ 'core.settings.disabled' | translate }}</ion-option>
|
||||
<ion-option value="10">{{ 600 | coreDuration }}</ion-option>
|
||||
<ion-option value="30">{{ 1800 | coreDuration }}</ion-option>
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
color: $toolbar-ios-button-color;
|
||||
}
|
||||
|
||||
.item-ios ion-spinner[item-start],
|
||||
.item-ios ion-spinner[item-end] {
|
||||
@include margin($item-ios-padding-icon-top, null, $item-ios-padding-icon-bottom, 0);
|
||||
}
|
||||
|
||||
// Highlights inside the input element.
|
||||
@if ($core-text-input-ios-show-highlight) {
|
||||
.card-ios, .list-ios {
|
||||
|
|
|
@ -16,6 +16,11 @@
|
|||
color: $toolbar-md-button-color;
|
||||
}
|
||||
|
||||
.item-md ion-spinner[item-start] + .item-inner,
|
||||
.item-md ion-spinner[item-start] + .item-input {
|
||||
@include margin-horizontal($item-md-padding-start + ($item-md-padding-start / 2) - 1, null);
|
||||
}
|
||||
|
||||
// Highlights inside the input element.
|
||||
@if ($core-text-input-md-show-highlight) {
|
||||
.card-md, .list-md {
|
||||
|
|
|
@ -57,6 +57,8 @@ import { CoreCoursesModule } from '../core/courses/courses.module';
|
|||
import { CoreFileUploaderModule } from '../core/fileuploader/fileuploader.module';
|
||||
import { CoreSharedFilesModule } from '../core/sharedfiles/sharedfiles.module';
|
||||
import { CoreCourseModule } from '../core/course/course.module';
|
||||
import { CoreSiteHomeModule } from '../core/sitehome/sitehome.module';
|
||||
import { CoreContentLinksModule } from '../core/contentlinks/contentlinks.module';
|
||||
|
||||
// Addon modules.
|
||||
import { AddonCalendarModule } from '../addon/calendar/calendar.module';
|
||||
|
@ -92,6 +94,8 @@ export function createTranslateLoader(http: HttpClient) {
|
|||
CoreFileUploaderModule,
|
||||
CoreSharedFilesModule,
|
||||
CoreCourseModule,
|
||||
CoreSiteHomeModule,
|
||||
CoreContentLinksModule,
|
||||
AddonCalendarModule
|
||||
],
|
||||
bootstrap: [IonicApp],
|
||||
|
|
|
@ -15,3 +15,8 @@
|
|||
.bar-buttons core-context-menu .button-clear-wp {
|
||||
color: $toolbar-wp-button-color;
|
||||
}
|
||||
|
||||
.item-wp ion-spinner[item-start] + .item-inner,
|
||||
.item-wp ion-spinner[item-start] + .item-input {
|
||||
@include margin-horizontal(($item-wp-padding-start / 2), null);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,8 @@ core-loading {
|
|||
}
|
||||
}
|
||||
|
||||
.scroll-content > .padding > core-loading > .core-loading-container,
|
||||
ion-content[padding] > .scroll-content > core-loading > .core-loading-container,
|
||||
.scroll-content > core-loading > .core-loading-container,
|
||||
ion-content > .scroll-content > core-loading > .core-loading-container,
|
||||
.core-loading-center .core-loading-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
|
|
@ -26,6 +26,14 @@ import { TranslateService } from '@ngx-translate/core';
|
|||
* <core-loading [message]="loadingMessage" [hideUntil]="dataLoaded">
|
||||
* <!-- CONTENT TO HIDE UNTIL LOADED -->
|
||||
* </core-loading>
|
||||
*
|
||||
* IMPORTANT: Due to how ng-content works in Angular, the content of core-loading will be executed as soon as your view
|
||||
* is loaded, even if the content hidden. So if you have the following code:
|
||||
* <core-loading [hideUntil]="dataLoaded"><my-component></my-component></core-loading>
|
||||
*
|
||||
* The component "my-component" will be initialized immediately, even if dataLoaded is false, but it will be hidden. If you want
|
||||
* your component to be initialized only if dataLoaded is true, then you should use ngIf:
|
||||
* <core-loading [hideUntil]="dataLoaded"><my-component *ngIf="dataLoaded"></my-component></core-loading>
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-loading',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<ion-item>
|
||||
<ion-label>{{ 'core.site' | translate }}</ion-label>
|
||||
<ion-select [(ngModel)]="selectedSite" (ngModelChange)="siteSelected.emit(selectedSite)">
|
||||
<ion-select [(ngModel)]="selectedSite" (ngModelChange)="siteSelected.emit(selectedSite)" interface="popover">
|
||||
<ion-option *ngFor="let site of sites" [value]="site.id">{{ site.fullNameAndSiteName }}</ion-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
|
|
@ -12,25 +12,33 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, Output, OnInit, OnDestroy, ElementRef, EventEmitter } from '@angular/core';
|
||||
import { Component, Input, Output, OnInit, OnDestroy, ElementRef, EventEmitter, ContentChild, TemplateRef,
|
||||
ViewChild } from '@angular/core';
|
||||
import { CoreTabsComponent } from './tabs';
|
||||
import { Content } from 'ionic-angular';
|
||||
|
||||
/**
|
||||
* A tab to use inside core-tabs. The content of this tab will be displayed when the tab is selected.
|
||||
*
|
||||
* You must provide either a title or an icon for the tab.
|
||||
*
|
||||
* The tab content MUST be surrounded by ng-template. This component uses ngTemplateOutlet instead of ng-content because the
|
||||
* latter executes all the code immediately. This means that all the tabs would be initialized as soon as your view is
|
||||
* loaded, leading to performance issues.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* <core-tabs selectedIndex="1">
|
||||
* <core-tab [title]="'core.courses.timeline' | translate" (ionSelect)="switchTab('timeline')">
|
||||
* <ng-template> <!-- This ng-template is required. -->
|
||||
* <!-- Tab contents. -->
|
||||
* </ng-template>
|
||||
* </core-tab>
|
||||
* </core-tabs>
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-tab',
|
||||
template: '<ng-content></ng-content>'
|
||||
template: '<ng-container *ngIf="loaded" [ngTemplateOutlet]="template"></ng-container>'
|
||||
})
|
||||
export class CoreTabComponent implements OnInit, OnDestroy {
|
||||
@Input() title?: string; // The tab title.
|
||||
|
@ -42,7 +50,11 @@ export class CoreTabComponent implements OnInit, OnDestroy {
|
|||
@Input() id?: string; // An ID to identify the tab.
|
||||
@Output() ionSelect: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>();
|
||||
|
||||
@ContentChild(TemplateRef) template: TemplateRef<any> // Template defined by the content.
|
||||
@ContentChild(Content) scroll: Content;
|
||||
|
||||
element: HTMLElement; // The core-tab element.
|
||||
loaded = false;
|
||||
|
||||
constructor(private tabs: CoreTabsComponent, element: ElementRef) {
|
||||
this.element = element.nativeElement;
|
||||
|
@ -61,4 +73,30 @@ export class CoreTabComponent implements OnInit, OnDestroy {
|
|||
ngOnDestroy() {
|
||||
this.tabs.removeTab(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Select tab.
|
||||
*/
|
||||
selectTab() {
|
||||
this.element.classList.add('selected');
|
||||
|
||||
this.loaded = true;
|
||||
this.ionSelect.emit(this);
|
||||
|
||||
// Setup tab scrolling.
|
||||
setTimeout(() => {
|
||||
if (this.scroll) {
|
||||
this.scroll.getScrollElement().onscroll = (e) => {
|
||||
this.tabs.showHideTabs(e);
|
||||
}
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unselect tab.
|
||||
*/
|
||||
unselectTab() {
|
||||
this.element.classList.remove('selected');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<div class="core-tabs-bar">
|
||||
<core-loading [hideUntil]="hideUntil" class="core-loading-center">
|
||||
<div class="core-tabs-bar" #topTabs>
|
||||
<ng-container *ngFor="let tab of tabs; let idx = index">
|
||||
<a *ngIf="tab.show" [attr.aria-selected]="selected == idx" (click)="selectTab(idx)">
|
||||
<ion-icon *ngIf="tab.icon" [name]="tab.icon"></ion-icon>
|
||||
|
@ -7,6 +8,7 @@
|
|||
</a>
|
||||
</ng-container>
|
||||
</div>
|
||||
<div #originalTabs>
|
||||
<div class="core-tabs-content-container" #originalTabs>
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</core-loading>
|
|
@ -1,7 +1,6 @@
|
|||
core-tabs {
|
||||
.core-tabs-bar {
|
||||
@include position(null, null, 0, 0);
|
||||
|
||||
z-index: $z-index-toolbar;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
|
@ -21,12 +20,29 @@ core-tabs {
|
|||
}
|
||||
}
|
||||
}
|
||||
.core-tabs-content-container {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&.tabs-hidden {
|
||||
.core-tabs-bar {
|
||||
display: none !important;
|
||||
}
|
||||
.core-tabs-content-container {
|
||||
padding-bottom: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
core-tab {
|
||||
display: none;
|
||||
height: 100%;
|
||||
|
||||
&.selected {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.scroll-content.no-scroll {
|
||||
overflow: hidden !important;
|
||||
}
|
||||
|
|
|
@ -12,8 +12,10 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, Output, EventEmitter, OnInit, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
|
||||
import { Component, Input, Output, EventEmitter, OnInit, OnChanges, AfterViewInit, ViewChild, ElementRef,
|
||||
SimpleChange } from '@angular/core';
|
||||
import { CoreTabComponent } from './tab';
|
||||
import { Content } from 'ionic-angular';
|
||||
|
||||
/**
|
||||
* This component displays some tabs that usually share data between them.
|
||||
|
@ -25,7 +27,9 @@ import { CoreTabComponent } from './tab';
|
|||
*
|
||||
* <core-tabs selectedIndex="1">
|
||||
* <core-tab [title]="'core.courses.timeline' | translate" (ionSelect)="switchTab('timeline')">
|
||||
* <ng-template> <!-- This ng-template is required, @see CoreTabComponent. -->
|
||||
* <!-- Tab contents. -->
|
||||
* </ng-template>
|
||||
* </core-tab>
|
||||
* </core-tabs>
|
||||
*
|
||||
|
@ -35,45 +39,65 @@ import { CoreTabComponent } from './tab';
|
|||
selector: 'core-tabs',
|
||||
templateUrl: 'tabs.html'
|
||||
})
|
||||
export class CoreTabsComponent implements OnInit, AfterViewInit {
|
||||
export class CoreTabsComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
@Input() selectedIndex?: number = 0; // Index of the tab to select.
|
||||
@Input() hideUntil: boolean; // Determine when should the contents be shown.
|
||||
@Output() ionChange: EventEmitter<CoreTabComponent> = new EventEmitter<CoreTabComponent>(); // Emitted when the tab changes.
|
||||
@ViewChild('originalTabs') originalTabsRef: ElementRef;
|
||||
@ViewChild('topTabs') topTabs: ElementRef;
|
||||
|
||||
tabs: CoreTabComponent[] = []; // List of tabs.
|
||||
selected: number; // Selected tab number.
|
||||
protected originalTabsContainer: HTMLElement; // The container of the original tabs. It will include each tab's content.
|
||||
protected initialized = false;
|
||||
protected afterViewInitTriggered = false;
|
||||
|
||||
constructor() {}
|
||||
protected topTabsElement: HTMLElement; // The container of the original tabs. It will include each tab's content.
|
||||
protected tabBarHeight;
|
||||
protected tabBarElement: HTMLElement; // Host element.
|
||||
protected tabsShown = true;
|
||||
protected scroll: HTMLElement; // Parent scroll element (if core-tabs is inside a ion-content).
|
||||
|
||||
constructor(element: ElementRef, content: Content) {
|
||||
this.tabBarElement = element.nativeElement;
|
||||
setTimeout(() => {
|
||||
if (content) {
|
||||
this.scroll = content.getScrollElement();
|
||||
}
|
||||
}, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.originalTabsContainer = this.originalTabsRef.nativeElement;
|
||||
this.topTabsElement = this.topTabs.nativeElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* View has been initialized.
|
||||
*/
|
||||
ngAfterViewInit() {
|
||||
let selectedIndex = this.selectedIndex || 0,
|
||||
selectedTab = this.tabs[selectedIndex];
|
||||
|
||||
if (!selectedTab.enabled || !selectedTab.show) {
|
||||
// The tab is not enabled or not shown. Get the first tab that is enabled.
|
||||
selectedTab = this.tabs.find((tab, index) => {
|
||||
if (tab.enabled && tab.show) {
|
||||
selectedIndex = index;
|
||||
return true;
|
||||
this.afterViewInitTriggered = true;
|
||||
if (!this.initialized && this.hideUntil) {
|
||||
// Tabs should be shown, initialize them.
|
||||
this.initializeTabs();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect changes on input properties.
|
||||
*/
|
||||
ngOnChanges(changes: {[name: string]: SimpleChange}) {
|
||||
// We need to wait for ngAfterViewInit because we need core-tab components to be executed.
|
||||
if (!this.initialized && this.hideUntil && this.afterViewInitTriggered) {
|
||||
// Tabs should be shown, initialize them.
|
||||
// Use a setTimeout so child core-tab update their inputs before initializing the tabs.
|
||||
setTimeout(() => {
|
||||
this.initializeTabs();
|
||||
});
|
||||
}
|
||||
|
||||
if (selectedTab) {
|
||||
this.selectTab(selectedIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -114,6 +138,57 @@ export class CoreTabsComponent implements OnInit, AfterViewInit {
|
|||
return this.tabs[this.selected];
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the tabs, determining the first tab to be shown.
|
||||
*/
|
||||
protected initializeTabs() : void {
|
||||
let selectedIndex = this.selectedIndex || 0,
|
||||
selectedTab = this.tabs[selectedIndex];
|
||||
|
||||
if (!selectedTab.enabled || !selectedTab.show) {
|
||||
// The tab is not enabled or not shown. Get the first tab that is enabled.
|
||||
selectedTab = this.tabs.find((tab, index) => {
|
||||
if (tab.enabled && tab.show) {
|
||||
selectedIndex = index;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
if (selectedTab) {
|
||||
this.selectTab(selectedIndex);
|
||||
}
|
||||
|
||||
// Setup tab scrolling.
|
||||
this.tabBarHeight = this.topTabsElement.offsetHeight;
|
||||
this.originalTabsContainer.style.paddingBottom = this.tabBarHeight + 'px';
|
||||
if (this.scroll) {
|
||||
this.scroll.classList.add('no-scroll');
|
||||
}
|
||||
|
||||
this.initialized = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show or hide the tabs. This is used when the user is scrolling inside a tab.
|
||||
*
|
||||
* @param {any} e Scroll event.
|
||||
*/
|
||||
showHideTabs(e: any) : void {
|
||||
if (e.target.scrollTop < this.tabBarHeight) {
|
||||
if (!this.tabsShown) {
|
||||
this.tabBarElement.classList.remove('tabs-hidden');
|
||||
this.tabsShown = true;
|
||||
}
|
||||
} else {
|
||||
if (this.tabsShown) {
|
||||
this.tabBarElement.classList.add('tabs-hidden');
|
||||
this.tabsShown = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a tab from the list of tabs.
|
||||
*
|
||||
|
@ -140,7 +215,7 @@ export class CoreTabsComponent implements OnInit, AfterViewInit {
|
|||
index = 0;
|
||||
}
|
||||
|
||||
const currenTab = this.getSelected(),
|
||||
const currentTab = this.getSelected(),
|
||||
newTab = this.tabs[index];
|
||||
|
||||
if (!newTab.enabled || !newTab.show) {
|
||||
|
@ -148,14 +223,13 @@ export class CoreTabsComponent implements OnInit, AfterViewInit {
|
|||
return;
|
||||
}
|
||||
|
||||
if (currenTab) {
|
||||
if (currentTab) {
|
||||
// Unselect previous selected tab.
|
||||
currenTab.element.classList.remove('selected');
|
||||
currentTab.unselectTab();
|
||||
}
|
||||
|
||||
this.selected = index;
|
||||
newTab.element.classList.add('selected');
|
||||
newTab.ionSelect.emit(newTab);
|
||||
newTab.selectTab();
|
||||
this.ionChange.emit(newTab);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,111 @@
|
|||
// (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 { CoreContentLinksHandler, CoreContentLinksAction } from '../providers/delegate';
|
||||
|
||||
/**
|
||||
* Base handler to be registered in CoreContentLinksHandler. It is useful to minimize the amount of
|
||||
* functions that handlers need to implement.
|
||||
*
|
||||
* It allows you to specify a "pattern" (RegExp) that will be used to check if the handler handles a URL and to get its site URL.
|
||||
*/
|
||||
export class CoreContentLinksHandlerBase implements CoreContentLinksHandler {
|
||||
/**
|
||||
* A name to identify the handler.
|
||||
* @type {string}
|
||||
*/
|
||||
name = 'CoreContentLinksHandlerBase';
|
||||
|
||||
/**
|
||||
* Handler's priority. The highest priority is treated first.
|
||||
* @type {number}
|
||||
*/
|
||||
priority = 0;
|
||||
|
||||
/**
|
||||
* Whether the isEnabled function should be called for all the users in a site. It should be true only if the isEnabled call
|
||||
* can return different values for different users in same site.
|
||||
* @type {boolean}
|
||||
*/
|
||||
checkAllUsers = false;
|
||||
|
||||
/**
|
||||
* Name of the feature this handler is related to.
|
||||
* It will be used to check if the feature is disabled (@see CoreSite.isFeatureDisabled).
|
||||
* @type {string}
|
||||
*/
|
||||
featureName = '';
|
||||
|
||||
/**
|
||||
* A pattern to use to detect if the handler handles a URL and to get its site URL. Required if "handles" and
|
||||
* "getSiteUrl" functions aren't overridden.
|
||||
* @type {RexExp}
|
||||
*/
|
||||
pattern?: RegExp;
|
||||
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
* @param {string[]} siteIds List of sites the URL belongs to.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||
*/
|
||||
getActions(siteIds: string[], url: string, params: any, courseId?: number) :
|
||||
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a URL is handled by this handler.
|
||||
*
|
||||
* @param {string} url The URL to check.
|
||||
* @return {boolean} Whether the URL is handled by this handler
|
||||
*/
|
||||
handles(url: string) : boolean {
|
||||
return this.pattern && url.search(this.pattern) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* If the URL is handled by this handler, return the site URL.
|
||||
*
|
||||
* @param {string} url The URL to check.
|
||||
* @return {string} Site URL if it is handled, undefined otherwise.
|
||||
*/
|
||||
getSiteUrl(url: string) : string {
|
||||
if (this.pattern) {
|
||||
var position = url.search(this.pattern);
|
||||
if (position > -1) {
|
||||
return url.substr(0, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled for a certain site (site + user) and a URL.
|
||||
* If not defined, defaults to true.
|
||||
*
|
||||
* @param {string} siteId The site ID.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site.
|
||||
*/
|
||||
isEnabled(siteId: string, url: string, params: any, courseId?: number) : boolean|Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// (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 { NavController } from 'ionic-angular';
|
||||
import { CoreContentLinksAction } from '../providers/delegate';
|
||||
import { CoreContentLinksHandlerBase } from './base-handler';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../providers/utils/dom';
|
||||
import { CoreCourseHelperProvider } from '../../course/providers/helper';
|
||||
|
||||
/**
|
||||
* Handler to handle URLs pointing to the grade of a module.
|
||||
*/
|
||||
export class CoreContentLinksModuleGradeHandler extends CoreContentLinksHandlerBase {
|
||||
|
||||
/**
|
||||
* Name of the addon as it's registered in course delegate. It'll be used to check if it's disabled.
|
||||
* @type {string}
|
||||
*/
|
||||
addon: string;
|
||||
|
||||
/**
|
||||
* Name of the module (assign, book, ...).
|
||||
* @type {string}
|
||||
*/
|
||||
modName: string;
|
||||
|
||||
/**
|
||||
* Whether the module can be reviewed in the app. If true, the handler needs to implement the goToReview function.
|
||||
* @type {boolean}
|
||||
*/
|
||||
canReview: boolean;
|
||||
|
||||
constructor(protected courseHelper: CoreCourseHelperProvider, protected domUtils: CoreDomUtilsProvider,
|
||||
protected sitesProvider: CoreSitesProvider) {
|
||||
super();
|
||||
|
||||
// Match the grade.php URL with an id param.
|
||||
this.pattern = new RegExp('\/mod\/' + this.modName + '\/grade\.php.*([\&\?]id=\\d+)');
|
||||
this.featureName = '$mmCourseDelegate_' + this.addon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
* @param {string[]} siteIds List of sites the URL belongs to.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||
*/
|
||||
getActions(siteIds: string[], url: string, params: any, courseId?: number) :
|
||||
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> {
|
||||
|
||||
courseId = courseId || params.courseid || params.cid;
|
||||
return [{
|
||||
action: (siteId, navCtrl?) : void => {
|
||||
// Check if userid is the site's current user.
|
||||
const modal = this.domUtils.showModalLoading();
|
||||
this.sitesProvider.getSite(siteId).then((site) => {
|
||||
if (!params.userid || params.userid == site.getUserId()) {
|
||||
// No user specified or current user. Navigate to module.
|
||||
this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId);
|
||||
} else if (this.canReview) {
|
||||
// Use the goToReview function.
|
||||
this.goToReview(url, params, courseId, siteId, navCtrl);
|
||||
} else {
|
||||
// Not current user and cannot review it in the app, open it in browser.
|
||||
site.openInBrowserWithAutoLogin(url);
|
||||
}
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the page to review.
|
||||
*
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} courseId Course ID related to the URL.
|
||||
* @param {string} siteId Site to use.
|
||||
* @param {NavController} [navCtrl] Nav Controller to use to navigate.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected goToReview(url: string, params: any, courseId: number, siteId: string, navCtrl?: NavController) : Promise<any> {
|
||||
// This function should be overridden.
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// (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 { CoreContentLinksAction } from '../providers/delegate';
|
||||
import { CoreContentLinksHandlerBase } from './base-handler';
|
||||
import { CoreCourseHelperProvider } from '../../course/providers/helper';
|
||||
|
||||
/**
|
||||
* Handler to handle URLs pointing to the index of a module.
|
||||
*/
|
||||
export class CoreContentLinksModuleIndexHandler extends CoreContentLinksHandlerBase {
|
||||
|
||||
/**
|
||||
* Name of the addon as it's registered in course delegate. It'll be used to check if it's disabled.
|
||||
* @type {string}
|
||||
*/
|
||||
addon: string;
|
||||
|
||||
/**
|
||||
* Name of the module (assign, book, ...).
|
||||
* @type {string}
|
||||
*/
|
||||
modName: string;
|
||||
|
||||
constructor(private courseHelper: CoreCourseHelperProvider) {
|
||||
super();
|
||||
|
||||
// Match the view.php URL with an id param.
|
||||
this.pattern = new RegExp('\/mod\/' + this.modName + '\/view\.php.*([\&\?]id=\\d+)');
|
||||
this.featureName = '$mmCourseDelegate_' + this.addon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
* @param {string[]} siteIds List of sites the URL belongs to.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||
*/
|
||||
getActions(siteIds: string[], url: string, params: any, courseId?: number) :
|
||||
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> {
|
||||
|
||||
courseId = courseId || params.courseid || params.cid;
|
||||
return [{
|
||||
action: (siteId, navCtrl?) => {
|
||||
this.courseHelper.navigateToModule(parseInt(params.id, 10), siteId, courseId);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
// (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 { CoreContentLinksDelegate } from './providers/delegate';
|
||||
import { CoreContentLinksHelperProvider } from './providers/helper';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
],
|
||||
providers: [
|
||||
CoreContentLinksDelegate,
|
||||
CoreContentLinksHelperProvider
|
||||
],
|
||||
exports: []
|
||||
})
|
||||
export class CoreContentLinksModule {}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"chooseaccount": "Choose account",
|
||||
"chooseaccounttoopenlink": "Choose an account to open the link with.",
|
||||
"confirmurlothersite": "This link belongs to another site. Do you want to open it?",
|
||||
"errornoactions": "Couldn't find an action to perform with this link.",
|
||||
"errornosites": "Couldn't find any site to handle this link."
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'core.contentlinks.chooseaccount' | translate }}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<core-loading [hideUntil]="loaded">
|
||||
<ion-list>
|
||||
<ion-item text-wrap>
|
||||
<p class="item-heading">{{ 'core.contentlinks.chooseaccounttoopenlink' | translate }}</p>
|
||||
<p>{{ url }}</p>
|
||||
</ion-item>
|
||||
<a ion-item *ngFor="let site of sites" (click)="siteClicked(site.id)">
|
||||
<img [src]="site.avatar" item-start>
|
||||
<h2>{{site.fullName}}</h2>
|
||||
<p><core-format-text clean="true" [text]="site.siteName"></core-format-text></p>
|
||||
<p>{{site.siteUrl}}</p>
|
||||
</a>
|
||||
<ion-item>
|
||||
<button ion-button block (click)="cancel()">{{ 'core.login.cancel' | translate }}</button>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,33 @@
|
|||
// (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 { IonicPageModule } from 'ionic-angular';
|
||||
import { CoreContentLinksChooseSitePage } from './choose-site';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||
import { CoreDirectivesModule } from '../../../../directives/directives.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreContentLinksChooseSitePage
|
||||
],
|
||||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
IonicPageModule.forChild(CoreContentLinksChooseSitePage),
|
||||
TranslateModule.forChild()
|
||||
]
|
||||
})
|
||||
export class CoreContentLinksChooseSitePageModule {}
|
|
@ -0,0 +1,95 @@
|
|||
// (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, OnInit } from '@angular/core';
|
||||
import { IonicPage, NavController, NavParams } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreContentLinksDelegate, CoreContentLinksAction } from '../../providers/delegate';
|
||||
import { CoreContentLinksHelperProvider } from '../../providers/helper';
|
||||
|
||||
/**
|
||||
* Page to display the list of sites to choose one to perform a content link action.
|
||||
*/
|
||||
@IonicPage({segment: 'core-content-links-choose-site'})
|
||||
@Component({
|
||||
selector: 'page-core-content-links-choose-site',
|
||||
templateUrl: 'choose-site.html',
|
||||
})
|
||||
export class CoreContentLinksChooseSitePage implements OnInit {
|
||||
|
||||
url: string;
|
||||
sites: any[];
|
||||
loaded: boolean;
|
||||
protected action: CoreContentLinksAction;
|
||||
|
||||
constructor(private navCtrl: NavController, navParams: NavParams, private contentLinksDelegate: CoreContentLinksDelegate,
|
||||
private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
|
||||
private contentLinksHelper: CoreContentLinksHelperProvider) {
|
||||
this.url = navParams.get('url');
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit() {
|
||||
if (!this.url) {
|
||||
return this.leaveView();
|
||||
}
|
||||
|
||||
// Get the action to perform.
|
||||
this.contentLinksDelegate.getActionsFor(this.url).then((actions) => {
|
||||
this.action = this.contentLinksHelper.getFirstValidAction(actions);
|
||||
if (!this.action) {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
// Get the sites that can perform the action.
|
||||
return this.sitesProvider.getSites(this.action.sites).then((sites) => {
|
||||
this.sites = sites;
|
||||
});
|
||||
}).catch(() => {
|
||||
this.domUtils.showErrorModal('core.contentlinks.errornosites', true);
|
||||
this.leaveView();
|
||||
}).finally(() => {
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel.
|
||||
*/
|
||||
cancel() : void {
|
||||
this.leaveView();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform the action on a certain site.
|
||||
*
|
||||
* @param {string} siteId Site ID.
|
||||
*/
|
||||
siteClicked(siteId: string) : void {
|
||||
this.action.action(siteId, this.navCtrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cancel and leave the view.
|
||||
*/
|
||||
protected leaveView() {
|
||||
this.sitesProvider.logout().finally(() => {
|
||||
this.navCtrl.setRoot('CoreLoginSitesPage');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,311 @@
|
|||
// (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 { NavController } from 'ionic-angular';
|
||||
import { CoreLoggerProvider } from '../../../providers/logger';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreUrlUtilsProvider } from '../../../providers/utils/url';
|
||||
import { CoreUtilsProvider } from '../../../providers/utils/utils';
|
||||
|
||||
/**
|
||||
* Interface that all handlers must implement.
|
||||
*/
|
||||
export interface CoreContentLinksHandler {
|
||||
/**
|
||||
* A name to identify the handler.
|
||||
* @type {string}
|
||||
*/
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Handler's priority. The highest priority is treated first.
|
||||
* @type {number}
|
||||
*/
|
||||
priority?: number;
|
||||
|
||||
/**
|
||||
* Whether the isEnabled function should be called for all the users in a site. It should be true only if the isEnabled call
|
||||
* can return different values for different users in same site.
|
||||
* @type {boolean}
|
||||
*/
|
||||
checkAllUsers?: boolean;
|
||||
|
||||
/**
|
||||
* Name of the feature this handler is related to.
|
||||
* It will be used to check if the feature is disabled (@see CoreSite.isFeatureDisabled).
|
||||
* @type {string}
|
||||
*/
|
||||
featureName?: string;
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
* @param {string[]} siteIds List of sites the URL belongs to.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||
*/
|
||||
getActions(siteIds: string[], url: string, params: any, courseId?: number) :
|
||||
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>;
|
||||
|
||||
/**
|
||||
* Check if a URL is handled by this handler.
|
||||
*
|
||||
* @param {string} url The URL to check.
|
||||
* @return {boolean} Whether the URL is handled by this handler
|
||||
*/
|
||||
handles(url: string) : boolean;
|
||||
|
||||
/**
|
||||
* If the URL is handled by this handler, return the site URL.
|
||||
*
|
||||
* @param {string} url The URL to check.
|
||||
* @return {string} Site URL if it is handled, undefined otherwise.
|
||||
*/
|
||||
getSiteUrl(url: string) : string;
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled for a certain site (site + user) and a URL.
|
||||
* If not defined, defaults to true.
|
||||
*
|
||||
* @param {string} siteId The site ID.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site.
|
||||
*/
|
||||
isEnabled?(siteId: string, url: string, params: any, courseId?: number) : boolean|Promise<boolean>;
|
||||
};
|
||||
|
||||
/**
|
||||
* Action to perform when a link is clicked.
|
||||
*/
|
||||
export interface CoreContentLinksAction {
|
||||
/**
|
||||
* A message to identify the action. Default: 'core.view'.
|
||||
* @type {string}
|
||||
*/
|
||||
message?: string;
|
||||
|
||||
/**
|
||||
* Name of the icon of the action. Default: 'eye'.
|
||||
* @type {string}
|
||||
*/
|
||||
icon?: string;
|
||||
|
||||
/**
|
||||
* IDs of the sites that support the action.
|
||||
* @type {string[]}
|
||||
*/
|
||||
sites?: string[];
|
||||
|
||||
/**
|
||||
* Action to perform when the link is clicked.
|
||||
*
|
||||
* @param {string} siteId The site ID.
|
||||
* @param {NavController} [navCtrl] Nav Controller to use to navigate.
|
||||
*/
|
||||
action(siteId: string, navCtrl?: NavController) : void;
|
||||
};
|
||||
|
||||
/**
|
||||
* Actions and priority for a handler and URL.
|
||||
*/
|
||||
export interface CoreContentLinksHandlerActions {
|
||||
/**
|
||||
* Handler's priority.
|
||||
* @type {number}
|
||||
*/
|
||||
priority: number;
|
||||
|
||||
/**
|
||||
* List of actions.
|
||||
* @type {CoreContentLinksAction[]}
|
||||
*/
|
||||
actions: CoreContentLinksAction[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Delegate to register handlers to handle links.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreContentLinksDelegate {
|
||||
protected logger;
|
||||
protected handlers: {[s: string]: CoreContentLinksHandler} = {}; // All registered handlers.
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider,
|
||||
private utils: CoreUtilsProvider) {
|
||||
this.logger = logger.getInstance('CoreContentLinksDelegate');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of possible actions to do for a URL.
|
||||
*
|
||||
* @param {string} url URL to handle.
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @param {string} [username] Username to use to filter sites.
|
||||
* @return {Promise<CoreContentLinksAction[]>} Promise resolved with the actions.
|
||||
*/
|
||||
getActionsFor(url: string, courseId?: number, username?: string) : Promise<CoreContentLinksAction[]> {
|
||||
if (!url) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
// Get the list of sites the URL belongs to.
|
||||
return this.sitesProvider.getSiteIdsFromUrl(url, true, username).then((siteIds) => {
|
||||
const linkActions: CoreContentLinksHandlerActions[] = [],
|
||||
promises = [],
|
||||
params = this.urlUtils.extractUrlParams(url);
|
||||
|
||||
for (let name in this.handlers) {
|
||||
const handler = this.handlers[name],
|
||||
checkAll = handler.checkAllUsers,
|
||||
isEnabledFn = this.isHandlerEnabled.bind(this, handler, url, params, courseId);
|
||||
|
||||
if (!handler.handles(url)) {
|
||||
// Invalid handler or it doesn't handle the URL. Stop.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Filter the site IDs using the isEnabled function.
|
||||
promises.push(this.utils.filterEnabledSites(siteIds, isEnabledFn, checkAll).then((siteIds) => {
|
||||
if (!siteIds.length) {
|
||||
// No sites supported, no actions.
|
||||
return;
|
||||
}
|
||||
|
||||
return Promise.resolve(handler.getActions(siteIds, url, params, courseId)).then((actions) => {
|
||||
if (actions && actions.length) {
|
||||
// Set default values if any value isn't supplied.
|
||||
actions.forEach((action) => {
|
||||
action.message = action.message || 'core.view';
|
||||
action.icon = action.icon || 'eye';
|
||||
action.sites = action.sites || siteIds;
|
||||
});
|
||||
|
||||
// Add them to the list.
|
||||
linkActions.push({
|
||||
priority: handler.priority,
|
||||
actions: actions
|
||||
});
|
||||
}
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
return this.utils.allPromises(promises).catch(() => {
|
||||
// Ignore errors.
|
||||
}).then(() => {
|
||||
// Sort link actions by priority.
|
||||
return this.sortActionsByPriority(linkActions);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the site URL if the URL is supported by any handler.
|
||||
*
|
||||
* @param {string} url URL to handle.
|
||||
* @return {string} Site URL if the URL is supported by any handler, undefined otherwise.
|
||||
*/
|
||||
getSiteUrl(url: string) : string {
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if any handler supports this URL.
|
||||
for (let name in this.handlers) {
|
||||
const handler = this.handlers[name],
|
||||
siteUrl = handler.getSiteUrl(url);
|
||||
|
||||
if (siteUrl) {
|
||||
return siteUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a handler is enabled for a certain site and URL.
|
||||
*
|
||||
* @param {CoreContentLinksHandler} handler Handler to check.
|
||||
* @param {string} url The URL to check.
|
||||
* @param {any} params The params of the URL
|
||||
* @param {number} courseId Course ID the URL belongs to (can be undefined).
|
||||
* @param {string} siteId The site ID to check.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: whether the handler is enabled.
|
||||
*/
|
||||
protected isHandlerEnabled(handler: CoreContentLinksHandler, url: string, params: any, courseId: number, siteId: string)
|
||||
: Promise<boolean> {
|
||||
let promise;
|
||||
|
||||
if (handler.featureName) {
|
||||
// Check if the feature is disabled.
|
||||
promise = this.sitesProvider.isFeatureDisabled(handler.featureName, siteId);
|
||||
} else {
|
||||
promise = Promise.resolve(false);
|
||||
}
|
||||
|
||||
return promise.then((disabled) => {
|
||||
if (disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!handler.isEnabled) {
|
||||
// isEnabled function not provided, assume it's enabled.
|
||||
return true;
|
||||
}
|
||||
|
||||
return handler.isEnabled(siteId, url, params, courseId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a handler.
|
||||
*
|
||||
* @param {CoreContentLinksHandler} handler The handler to register.
|
||||
* @return {boolean} True if registered successfully, false otherwise.
|
||||
*/
|
||||
registerHandler(handler: CoreContentLinksHandler) : boolean {
|
||||
if (typeof this.handlers[handler.name] !== 'undefined') {
|
||||
this.logger.log(`Addon '${handler.name}' already registered`);
|
||||
return false;
|
||||
}
|
||||
this.logger.log(`Registered addon '${handler.name}'`);
|
||||
this.handlers[handler.name] = handler;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort actions by priority.
|
||||
*
|
||||
* @param {CoreContentLinksHandlerActions[]} actions Actions to sort.
|
||||
* @return {CoreContentLinksAction[]} Sorted actions.
|
||||
*/
|
||||
protected sortActionsByPriority(actions: CoreContentLinksHandlerActions[]) : CoreContentLinksAction[] {
|
||||
let sorted: CoreContentLinksAction[] = [];
|
||||
|
||||
// Sort by priority.
|
||||
actions = actions.sort((a, b) => {
|
||||
return a.priority >= b.priority ? 1 : -1;
|
||||
});
|
||||
|
||||
// Fill result array.
|
||||
actions.forEach((entry) => {
|
||||
sorted = sorted.concat(entry.actions);
|
||||
});
|
||||
return sorted;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,240 @@
|
|||
// (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 { NavController } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreAppProvider } from '../../../providers/app';
|
||||
import { CoreEventsProvider } from '../../../providers/events';
|
||||
import { CoreInitDelegate } from '../../../providers/init';
|
||||
import { CoreLoggerProvider } from '../../../providers/logger';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../providers/utils/dom';
|
||||
import { CoreUrlUtilsProvider } from '../../../providers/utils/url';
|
||||
import { CoreLoginHelperProvider } from '../../login/providers/helper';
|
||||
import { CoreContentLinksDelegate, CoreContentLinksAction } from './delegate';
|
||||
import { CoreConstants } from '../../constants';
|
||||
import { CoreConfigConstants } from '../../../configconstants';
|
||||
|
||||
/**
|
||||
* Service that provides some features regarding content links.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreContentLinksHelperProvider {
|
||||
protected logger;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider,
|
||||
private contentLinksDelegate: CoreContentLinksDelegate, private appProvider: CoreAppProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private translate: TranslateService,
|
||||
private initDelegate: CoreInitDelegate, eventsProvider: CoreEventsProvider) {
|
||||
this.logger = logger.getInstance('CoreContentLinksHelperProvider');
|
||||
|
||||
// Listen for app launched URLs. If we receive one, check if it's a content link.
|
||||
eventsProvider.on(CoreEventsProvider.APP_LAUNCHED_URL, this.handleCustomUrl.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the first valid action in a list of actions.
|
||||
*
|
||||
* @param {CoreContentLinksAction[]} actions List of actions.
|
||||
* @return {CoreContentLinksAction} First valid action. Returns undefined if no valid action found.
|
||||
*/
|
||||
getFirstValidAction(actions: CoreContentLinksAction[]) : CoreContentLinksAction {
|
||||
if (actions) {
|
||||
for (let i = 0; i < actions.length; i++) {
|
||||
const action = actions[i];
|
||||
if (action && action.sites && action.sites.length) {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Goes to a certain page in a certain site. If the site is current site it will perform a regular navigation,
|
||||
* otherwise it will 'redirect' to the other site.
|
||||
*
|
||||
* @param {NavController} navCtrl The NavController instance to use.
|
||||
* @param {string} pageName Name of the page to go.
|
||||
* @param {any} [pageParams] Params to send to the page.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
*/
|
||||
goInSite(navCtrl: NavController, pageName: string, pageParams: any, siteId?: string) : void {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
if (siteId == this.sitesProvider.getCurrentSiteId()) {
|
||||
navCtrl.push(pageName, pageParams);
|
||||
} else {
|
||||
this.loginHelper.redirect(pageName, pageParams, siteId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Go to the page to choose a site.
|
||||
*
|
||||
* @param {string} url URL to treat.
|
||||
*/
|
||||
goToChooseSite(url: string) : void {
|
||||
this.appProvider.getRootNavController().setRoot('CoreContentLinksChooseSitePage', {url: url});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a URL received by Custom URL Scheme.
|
||||
*
|
||||
* @param {string} url URL to handle.
|
||||
* @return {boolean} True if the URL should be handled by this component, false otherwise.
|
||||
*/
|
||||
handleCustomUrl(url: string) : boolean {
|
||||
const contentLinksScheme = CoreConfigConstants.customurlscheme + '://link';
|
||||
if (url.indexOf(contentLinksScheme) == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
url = decodeURIComponent(url);
|
||||
|
||||
// App opened using custom URL scheme.
|
||||
this.logger.debug('Treating custom URL scheme: ' + url);
|
||||
|
||||
let modal = this.domUtils.showModalLoading(),
|
||||
username;
|
||||
|
||||
// Delete the scheme from the URL.
|
||||
url = url.replace(contentLinksScheme + '=', '');
|
||||
|
||||
// Detect if there's a user specified.
|
||||
username = this.urlUtils.getUsernameFromUrl(url);
|
||||
if (username) {
|
||||
url = url.replace(username + '@', ''); // Remove the username from the URL.
|
||||
}
|
||||
|
||||
// Wait for the app to be ready.
|
||||
this.initDelegate.ready().then(() => {
|
||||
// Check if the site is stored.
|
||||
return this.sitesProvider.getSiteIdsFromUrl(url, false, username);
|
||||
}).then((siteIds) => {
|
||||
if (siteIds.length) {
|
||||
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
|
||||
return this.handleLink(url, username).then((treated) => {
|
||||
if (!treated) {
|
||||
this.domUtils.showErrorModal('core.contentlinks.errornoactions', true);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Get the site URL.
|
||||
const siteUrl = this.contentLinksDelegate.getSiteUrl(url);
|
||||
if (!siteUrl) {
|
||||
this.domUtils.showErrorModal('core.login.invalidsite', true);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check that site exists.
|
||||
return this.sitesProvider.checkSite(siteUrl).then((result) => {
|
||||
// Site exists. We'll allow to add it.
|
||||
let promise,
|
||||
ssoNeeded = this.loginHelper.isSSOLoginNeeded(result.code),
|
||||
hasRemoteAddonsLoaded = false,
|
||||
pageName = 'CoreLoginCredentialsPage',
|
||||
pageParams = {
|
||||
siteUrl: result.siteUrl,
|
||||
username: username,
|
||||
urlToOpen: url,
|
||||
siteConfig: result.config
|
||||
};
|
||||
|
||||
modal.dismiss(); // Dismiss modal so it doesn't collide with confirms.
|
||||
|
||||
if (!this.sitesProvider.isLoggedIn()) {
|
||||
// Not logged in, no need to confirm. If SSO the confirm will be shown later.
|
||||
promise = Promise.resolve();
|
||||
} else {
|
||||
// Ask the user before changing site.
|
||||
const confirmMsg = this.translate.instant('core.contentlinks.confirmurlothersite');
|
||||
promise = this.domUtils.showConfirm(confirmMsg).then(() => {
|
||||
if (!ssoNeeded) {
|
||||
// hasRemoteAddonsLoaded = $mmAddonManager.hasRemoteAddonsLoaded(); @todo
|
||||
if (hasRemoteAddonsLoaded) {
|
||||
// Store the redirect since logout will restart the app.
|
||||
this.appProvider.storeRedirect(CoreConstants.NO_SITE_ID, pageName, pageParams);
|
||||
}
|
||||
|
||||
return this.sitesProvider.logout().catch(() => {
|
||||
// Ignore errors (shouldn't happen).
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
if (ssoNeeded) {
|
||||
this.loginHelper.confirmAndOpenBrowserForSSOLogin(
|
||||
result.siteUrl, result.code, result.service, result.config && result.config.launchurl);
|
||||
} else if (!hasRemoteAddonsLoaded) {
|
||||
this.appProvider.getRootNavController().setRoot(pageName, pageParams);
|
||||
}
|
||||
});
|
||||
|
||||
}).catch((error) => {
|
||||
if (error) {
|
||||
this.domUtils.showErrorModal(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a link.
|
||||
*
|
||||
* @param {string} url URL to handle.
|
||||
* @param {string} [username] Username related with the URL. E.g. in 'http://myuser@m.com', url would be 'http://m.com' and
|
||||
* the username 'myuser'. Don't use it if you don't want to filter by username.
|
||||
* @param {NavController} [navCtrl] Nav Controller to use to navigate.
|
||||
* @return {Promise<boolean>} Promise resolved with a boolean: true if URL was treated, false otherwise.
|
||||
*/
|
||||
handleLink(url: string, username?: string, navCtrl?: NavController) : Promise<boolean> {
|
||||
// Check if the link should be treated by some component/addon.
|
||||
return this.contentLinksDelegate.getActionsFor(url, undefined, username).then((actions) => {
|
||||
const action = this.getFirstValidAction(actions);
|
||||
if (action) {
|
||||
if (!this.sitesProvider.isLoggedIn()) {
|
||||
// No current site. Perform the action if only 1 site found, choose the site otherwise.
|
||||
if (action.sites.length == 1) {
|
||||
action.action(action.sites[0], navCtrl);
|
||||
} else {
|
||||
this.goToChooseSite(url);
|
||||
}
|
||||
} else if (action.sites.length == 1 && action.sites[0] == this.sitesProvider.getCurrentSiteId()) {
|
||||
// Current site.
|
||||
action.action(action.sites[0], navCtrl);
|
||||
} else {
|
||||
// Not current site or more than one site. Ask for confirmation.
|
||||
this.domUtils.showConfirm(this.translate.instant('core.contentlinks.confirmurlothersite')).then(() => {
|
||||
if (action.sites.length == 1) {
|
||||
action.action(action.sites[0], navCtrl);
|
||||
} else {
|
||||
this.goToChooseSite(url);
|
||||
}
|
||||
});
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}).catch(() => {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -12,7 +12,7 @@
|
|||
<!-- Section selector. -->
|
||||
<div *ngIf="!componentInstances.sectionSelector && displaySectionSelector && sections && sections.length" no-padding class="clearfix">
|
||||
<!-- @todo: How to display availabilityinfo and not visible messages? -->
|
||||
<ion-select [ngModel]="selectedSection" (ngModelChange)="sectionChanged($event)" [compareWith]="compareSections" [selectOptions]="selectOptions" float-start>
|
||||
<ion-select [ngModel]="selectedSection" (ngModelChange)="sectionChanged($event)" [compareWith]="compareSections" [selectOptions]="selectOptions" float-start interface="popover">
|
||||
<ion-option *ngFor="let section of sections" [value]="section">{{section.formattedName || section.name}}</ion-option>
|
||||
</ion-select>
|
||||
<!-- Section download. -->
|
||||
|
|
|
@ -42,6 +42,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
@Input() course: any; // The course to render.
|
||||
@Input() sections: any[]; // List of course sections.
|
||||
@Input() downloadEnabled?: boolean; // Whether the download of sections and modules is enabled.
|
||||
@Input() initialSectionId?: number; // The section to load first (by ID).
|
||||
@Input() initialSectionNumber?: number; // The section to load first (by number).
|
||||
@Output() completionChanged?: EventEmitter<void>; // Will emit an event when any module completion changes.
|
||||
|
||||
// Get the containers where to inject dynamic components. We use a setter because they might be inside a *ngIf.
|
||||
|
@ -142,11 +144,24 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
ngOnChanges(changes: {[name: string]: SimpleChange}) {
|
||||
if (changes.sections && this.sections) {
|
||||
if (!this.selectedSection) {
|
||||
// There is no selected section yet, calculate which one to get.
|
||||
// There is no selected section yet, calculate which one to load.
|
||||
if (this.initialSectionId || this.initialSectionNumber) {
|
||||
// We have an input indicating the section ID to load. Search the section.
|
||||
for (let i = 0; i < this.sections.length; i++) {
|
||||
let section = this.sections[i];
|
||||
if (section.id == this.initialSectionId || section.section == this.initialSectionNumber) {
|
||||
this.loaded = true;
|
||||
this.sectionChanged(section);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No section specified, get current section.
|
||||
this.cfDelegate.getCurrentSection(this.course, this.sections).then((section) => {
|
||||
this.loaded = true;
|
||||
this.sectionChanged(section);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// We have a selected section, but the list has changed. Search the section in the list.
|
||||
let newSection;
|
||||
|
@ -214,6 +229,8 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
|
|||
// Set the Input data.
|
||||
this.componentInstances[type].course = this.course;
|
||||
this.componentInstances[type].sections = this.sections;
|
||||
this.componentInstances[type].initialSectionId = this.initialSectionId;
|
||||
this.componentInstances[type].initialSectionNumber = this.initialSectionNumber;
|
||||
this.componentInstances[type].downloadEnabled = this.downloadEnabled;
|
||||
|
||||
this.cdr.detectChanges(); // The instances are used in ngIf, tell Angular that something has changed.
|
||||
|
|
|
@ -21,6 +21,6 @@
|
|||
<a aria-selected="true">{{ 'core.course.contents' | translate }}</a>
|
||||
<a *ngFor="let handler of courseHandlers">{{ handler.data.title || translate }}</a>
|
||||
</div>
|
||||
<core-course-format [course]="course" [sections]="sections" [downloadEnabled]="downloadEnabled" (completionChanged)="onCompletionChange()"></core-course-format>
|
||||
<core-course-format [course]="course" [sections]="sections" [initialSectionId]="sectionId" [initialSectionNumber]="sectionNumber" [downloadEnabled]="downloadEnabled" (completionChanged)="onCompletionChange()"></core-course-format>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Component, ViewChild, OnDestroy } from '@angular/core';
|
||||
import { IonicPage, NavParams, Content } from 'ionic-angular';
|
||||
import { IonicPage, NavParams, Content, NavController } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreEventsProvider } from '../../../../providers/events';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
|
@ -39,6 +39,8 @@ export class CoreCourseSectionPage implements OnDestroy {
|
|||
title: string;
|
||||
course: any;
|
||||
sections: any[];
|
||||
sectionId: number;
|
||||
sectionNumber: number;
|
||||
courseHandlers: CoreCoursesHandlerToDisplay[];
|
||||
dataLoaded: boolean;
|
||||
downloadEnabled: boolean;
|
||||
|
@ -47,18 +49,18 @@ export class CoreCourseSectionPage implements OnDestroy {
|
|||
prefetchCourseIcon: 'spinner'
|
||||
};
|
||||
|
||||
protected moduleId;
|
||||
protected completionObserver;
|
||||
protected courseStatusObserver;
|
||||
protected isDestroyed = false;
|
||||
|
||||
constructor(navParams: NavParams, private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
|
||||
constructor(private navParams: NavParams, private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider,
|
||||
private courseFormatDelegate: CoreCourseFormatDelegate, private coursesDelegate: CoreCoursesDelegate,
|
||||
private translate: TranslateService, private courseHelper: CoreCourseHelperProvider, eventsProvider: CoreEventsProvider,
|
||||
private textUtils: CoreTextUtilsProvider, private coursesProvider: CoreCoursesProvider,
|
||||
sitesProvider: CoreSitesProvider) {
|
||||
sitesProvider: CoreSitesProvider, private navCtrl: NavController) {
|
||||
this.course = navParams.get('course');
|
||||
this.moduleId = navParams.get('moduleId');
|
||||
this.sectionId = navParams.get('sectionId');
|
||||
this.sectionNumber = navParams.get('sectionNumber');
|
||||
|
||||
// Get the title to display. We dont't have sections yet.
|
||||
this.title = courseFormatDelegate.getCourseTitle(this.course);
|
||||
|
@ -81,9 +83,14 @@ export class CoreCourseSectionPage implements OnDestroy {
|
|||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad() {
|
||||
|
||||
let module = this.navParams.get('module');
|
||||
if (module) {
|
||||
this.courseHelper.openModule(this.navCtrl, module, this.course.id, this.sectionId);
|
||||
}
|
||||
|
||||
this.loadData().finally(() => {
|
||||
this.dataLoaded = true;
|
||||
delete this.moduleId; // Only load module automatically the first time.
|
||||
|
||||
// Determine the course prefetch status.
|
||||
this.determineCoursePrefetchIcon().then(() => {
|
||||
|
@ -133,7 +140,7 @@ export class CoreCourseSectionPage implements OnDestroy {
|
|||
promises.push(promise.then((completionStatus) => {
|
||||
// Get all the sections.
|
||||
promises.push(this.courseProvider.getSections(this.course.id, false, true).then((sections) => {
|
||||
this.courseHelper.addHandlerDataForModules(sections, this.course.id, this.moduleId, completionStatus);
|
||||
this.courseHelper.addHandlerDataForModules(sections, this.course.id, completionStatus);
|
||||
|
||||
// Format the name of each section and check if it has content.
|
||||
this.sections = sections.map((section) => {
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreFilepoolProvider } from '../../../providers/filepool';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
|
@ -21,10 +22,13 @@ import { CoreTextUtilsProvider } from '../../../providers/utils/text';
|
|||
import { CoreTimeUtilsProvider } from '../../../providers/utils/time';
|
||||
import { CoreUtilsProvider } from '../../../providers/utils/utils';
|
||||
import { CoreCoursesDelegate, CoreCoursesHandlerToDisplay } from '../../courses/providers/delegate';
|
||||
import { CoreSiteHomeProvider } from '../../sitehome/providers/sitehome';
|
||||
import { CoreCourseProvider } from './course';
|
||||
import { CoreCourseModuleDelegate } from './module-delegate';
|
||||
import { CoreCourseModulePrefetchDelegate, CoreCourseModulePrefetchHandler } from './module-prefetch-delegate';
|
||||
import { CoreLoginHelperProvider } from '../../login/providers/helper';
|
||||
import { CoreConstants } from '../../constants';
|
||||
import { CoreSite } from '../../../classes/site';
|
||||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
|
@ -109,7 +113,8 @@ export class CoreCourseHelperProvider {
|
|||
private moduleDelegate: CoreCourseModuleDelegate, private prefetchDelegate: CoreCourseModulePrefetchDelegate,
|
||||
private filepoolProvider: CoreFilepoolProvider, private sitesProvider: CoreSitesProvider,
|
||||
private textUtils: CoreTextUtilsProvider, private timeUtils: CoreTimeUtilsProvider,
|
||||
private utils: CoreUtilsProvider, private translate: TranslateService, private coursesDelegate: CoreCoursesDelegate) {}
|
||||
private utils: CoreUtilsProvider, private translate: TranslateService, private coursesDelegate: CoreCoursesDelegate,
|
||||
private loginHelper: CoreLoginHelperProvider, private siteHomeProvider: CoreSiteHomeProvider) {}
|
||||
|
||||
/**
|
||||
* This function treats every module on the sections provided to load the handler data, treat completion
|
||||
|
@ -117,11 +122,10 @@ export class CoreCourseHelperProvider {
|
|||
*
|
||||
* @param {any[]} sections List of sections to treat modules.
|
||||
* @param {number} courseId Course ID of the modules.
|
||||
* @param {number} [moduleId] Module to navigate to if needed.
|
||||
* @param {any[]} [completionStatus] List of completion status.
|
||||
* @return {boolean} Whether the sections have content.
|
||||
*/
|
||||
addHandlerDataForModules(sections: any[], courseId: number, moduleId?: number, completionStatus?: any) {
|
||||
addHandlerDataForModules(sections: any[], courseId: number, completionStatus?: any) {
|
||||
let hasContent = false;
|
||||
|
||||
sections.forEach((section) => {
|
||||
|
@ -139,11 +143,6 @@ export class CoreCourseHelperProvider {
|
|||
module.completionstatus = completionStatus[module.id];
|
||||
module.completionstatus.courseId = courseId;
|
||||
}
|
||||
|
||||
if (module.id == moduleId) {
|
||||
// This is the module we're looking for. Open it.
|
||||
module.handlerData.action(new Event('click'), module, courseId);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -578,6 +577,86 @@ export class CoreCourseHelperProvider {
|
|||
return 'Section-' + section.id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigate to a module.
|
||||
*
|
||||
* @param {number} moduleId Module's ID.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {number} [courseId] Course ID. If not defined we'll try to retrieve it from the site.
|
||||
* @param {number} [sectionId] Section the module belongs to. If not defined we'll try to retrieve it from the site.
|
||||
* @return {Promise<void>} Promise resolved when done.
|
||||
*/
|
||||
navigateToModule(moduleId: number, siteId?: string, courseId?: number, sectionId?: number) : Promise<void> {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
|
||||
let modal = this.domUtils.showModalLoading(),
|
||||
promise,
|
||||
site: CoreSite;
|
||||
|
||||
if (courseId && sectionId) {
|
||||
// No need to retrieve more data.
|
||||
promise = Promise.resolve();
|
||||
} else if (!courseId) {
|
||||
// We don't have courseId.
|
||||
promise = this.courseProvider.getModuleBasicInfo(moduleId, siteId).then((module) => {
|
||||
courseId = module.course;
|
||||
sectionId = module.section;
|
||||
});
|
||||
} else {
|
||||
// We don't have sectionId but we have courseId.
|
||||
promise = this.courseProvider.getModuleSectionId(moduleId, siteId).then((id) => {
|
||||
sectionId = id;
|
||||
});
|
||||
}
|
||||
|
||||
return promise.then(() => {
|
||||
// Get the site.
|
||||
return this.sitesProvider.getSite(siteId);
|
||||
}).then((s) => {
|
||||
site = s;
|
||||
|
||||
// Get the module.
|
||||
return this.courseProvider.getModule(moduleId, courseId, sectionId, false, false, siteId);
|
||||
}).then((module) => {
|
||||
const params = {
|
||||
course: {id: courseId},
|
||||
module: module,
|
||||
sectionId: sectionId
|
||||
};
|
||||
|
||||
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId);
|
||||
|
||||
if (courseId == site.getSiteHomeId()) {
|
||||
// Check if site home is available.
|
||||
return this.siteHomeProvider.isAvailable().then(() => {
|
||||
this.loginHelper.redirect('CoreSiteHomeIndexPage', params, siteId);
|
||||
});
|
||||
} else {
|
||||
this.loginHelper.redirect('CoreCourseSectionPage', params, siteId);
|
||||
}
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.course.errorgetmodule', true);
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Open a module.
|
||||
*
|
||||
* @param {NavController} navCtrl The NavController to use.
|
||||
* @param {any} module The module to open.
|
||||
* @param {number} courseId The course ID of the module.
|
||||
* @param {number} [sectionId] The section ID of the module.
|
||||
*/
|
||||
openModule(navCtrl: NavController, module: any, courseId: number, sectionId?: number) : void {
|
||||
if (!module.handlerData) {
|
||||
module.handlerData = this.moduleDelegate.getModuleDataFor(module.modname, module, courseId, sectionId);
|
||||
}
|
||||
|
||||
module.handlerData.action(new Event('click'), navCtrl, module, courseId, {animate: false});
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefetch all the activities in a course and also the course addons.
|
||||
*
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { NavController, NavOptions } from 'ionic-angular';
|
||||
import { CoreEventsProvider } from '../../../providers/events';
|
||||
import { CoreLoggerProvider } from '../../../providers/logger';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
|
@ -104,8 +104,9 @@ export interface CoreCourseModuleHandlerData {
|
|||
* @param {NavController} navCtrl NavController instance.
|
||||
* @param {any} module The module object.
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {NavOptions} [options] Options for the navigation.
|
||||
*/
|
||||
action?(event: Event, navCtrl: NavController, module: any, courseId: number) : void;
|
||||
action?(event: Event, navCtrl: NavController, module: any, courseId: number, options?: NavOptions) : void;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -208,11 +209,11 @@ export class CoreCourseModuleDelegate {
|
|||
icon: this.courseProvider.getModuleIconSrc(module.modname),
|
||||
title: module.name,
|
||||
class: 'core-course-default-handler core-course-module-' + module.modname + '-handler',
|
||||
action: (event: Event, navCtrl: NavController, module: any, courseId: number) => {
|
||||
action: (event: Event, navCtrl: NavController, module: any, courseId: number, options?: NavOptions) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
navCtrl.push('CoreCourseUnsupportedModulePage', {module: module});
|
||||
navCtrl.push('CoreCourseUnsupportedModulePage', {module: module}, options);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -13,11 +13,13 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Component, Input, Output, OnChanges, EventEmitter, SimpleChange } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreTextUtilsProvider } from '../../../../providers/utils/text';
|
||||
import { CoreUtilsProvider } from '../../../../providers/utils/utils';
|
||||
import { CoreCourseProvider } from '../../../course/providers/course';
|
||||
import { CoreContentLinksHelperProvider } from '../../../contentlinks/providers/helper';
|
||||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
|
@ -41,9 +43,9 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
|
|||
next30Days: any[] = [];
|
||||
future: any[] = [];
|
||||
|
||||
constructor(private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider,
|
||||
constructor(private navCtrl: NavController, private utils: CoreUtilsProvider, private textUtils: CoreTextUtilsProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider,
|
||||
private courseProvider: CoreCourseProvider) {
|
||||
private courseProvider: CoreCourseProvider, private contentLinksHelper: CoreContentLinksHelperProvider) {
|
||||
this.loadMore = new EventEmitter();
|
||||
}
|
||||
|
||||
|
@ -100,9 +102,6 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
|
|||
loadMoreEvents() {
|
||||
this.loadingMore = true;
|
||||
this.loadMore.emit();
|
||||
// this.loadMore().finally(function() {
|
||||
// scope.loadingMore = false;
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,19 +118,14 @@ export class CoreCoursesOverviewEventsComponent implements OnChanges {
|
|||
url = this.textUtils.decodeHTMLEntities(url);
|
||||
|
||||
let modal = this.domUtils.showModalLoading();
|
||||
this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url).finally(() => {
|
||||
this.contentLinksHelper.handleLink(url, undefined, this.navCtrl).then((treated) => {
|
||||
if (!treated) {
|
||||
return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
|
||||
}
|
||||
}).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
|
||||
// @todo
|
||||
// $mmContentLinksHelper.handleLink(url).then((treated) => {
|
||||
// if (!treated) {
|
||||
// return this.sitesProvider.getCurrentSite().openInBrowserWithAutoLoginIfSameSite(url);
|
||||
// }
|
||||
// }).finally(() => {
|
||||
// modal.dismiss();
|
||||
// });
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,14 @@
|
|||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CoreCoursesProvider } from './providers/courses';
|
||||
import { CoreCoursesMainMenuHandler } from './providers/handlers';
|
||||
import { CoreCoursesMainMenuHandler } from './providers/mainmenu-handler';
|
||||
import { CoreCoursesMyOverviewProvider } from './providers/my-overview';
|
||||
import { CoreCoursesDelegate } from './providers/delegate';
|
||||
import { CoreCoursesCourseLinkHandler } from './providers/course-link-handler';
|
||||
import { CoreCoursesIndexLinkHandler } from './providers/courses-index-link-handler';
|
||||
import { CoreCoursesMyOverviewLinkHandler } from './providers/my-overview-link-handler';
|
||||
import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate';
|
||||
import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
|
@ -27,12 +31,21 @@ import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate';
|
|||
CoreCoursesProvider,
|
||||
CoreCoursesMainMenuHandler,
|
||||
CoreCoursesMyOverviewProvider,
|
||||
CoreCoursesDelegate
|
||||
CoreCoursesDelegate,
|
||||
CoreCoursesCourseLinkHandler,
|
||||
CoreCoursesIndexLinkHandler,
|
||||
CoreCoursesMyOverviewLinkHandler
|
||||
],
|
||||
exports: []
|
||||
})
|
||||
export class CoreCoursesModule {
|
||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, mainMenuHandler: CoreCoursesMainMenuHandler) {
|
||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, contentLinksDelegate: CoreContentLinksDelegate,
|
||||
mainMenuHandler: CoreCoursesMainMenuHandler, courseLinkHandler: CoreCoursesCourseLinkHandler,
|
||||
indexLinkHandler: CoreCoursesIndexLinkHandler, myOverviewLinkHandler: CoreCoursesMyOverviewLinkHandler) {
|
||||
mainMenuDelegate.registerHandler(mainMenuHandler);
|
||||
|
||||
contentLinksDelegate.registerHandler(courseLinkHandler);
|
||||
contentLinksDelegate.registerHandler(indexLinkHandler);
|
||||
contentLinksDelegate.registerHandler(myOverviewLinkHandler);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@
|
|||
<ion-icon name="folder" item-start></ion-icon>
|
||||
<h2><core-format-text [text]="category.name"></core-format-text></h2>
|
||||
<ion-badge item-end *ngIf="category.coursecount > 0" color="light">{{category.coursecount}}</ion-badge>
|
||||
<ion-icon item-end name="arrow-forward" md="ios-arrow-forward" class="icon-accessory"></ion-icon>
|
||||
</a>
|
||||
</section>
|
||||
</div>
|
||||
|
|
|
@ -19,11 +19,19 @@
|
|||
</ion-refresher>
|
||||
|
||||
<core-loading [hideUntil]="coursesLoaded">
|
||||
<ion-item *ngIf="showFilter" class="item-transparent">
|
||||
<ion-icon name="funnel" class="placeholder-icon" item-start></ion-icon>
|
||||
<ion-input type="text" name="filter" placeholder="{{ 'core.courses.filtermycourses' | translate }}" [(ngModel)]="filter" (ngModelChange)="filterChanged($event)"></ion-input>
|
||||
<div no-padding padding-bottom *ngIf="showFilter">
|
||||
<ion-item class="item-transparent">
|
||||
<ion-label item-start><ion-icon name="funnel" class="placeholder-icon"></ion-icon></ion-label>
|
||||
<ion-input type="text" name="filter" clearInput [(ngModel)]="filter" (ngModelChange)="filterChanged($event)" [placeholder]="'core.courses.filtermycourses' | translate"></ion-input>
|
||||
</ion-item>
|
||||
<core-courses-course-progress *ngFor="let course of filteredCourses" [course]="course" showSummary="true"></core-courses-course-progress>
|
||||
</div>
|
||||
<ion-grid no-padding>
|
||||
<ion-row no-padding>
|
||||
<ion-col *ngFor="let course of filteredCourses" no-padding col-12 col-sm-6 col-md-6 col-lg-4 col-xl-4 align-self-stretch>
|
||||
<core-courses-course-progress [course]="course" class="core-courseoverview"></core-courses-course-progress>
|
||||
</ion-col>
|
||||
</ion-row>
|
||||
</ion-grid>
|
||||
<core-empty-box *ngIf="!courses || !courses.length" icon="ionic" [message]="'core.courses.nocourses' | translate">
|
||||
<p *ngIf="searchEnabled">{{ 'core.courses.searchcoursesadvice' | translate }}</p>
|
||||
</core-empty-box>
|
||||
|
|
|
@ -12,16 +12,25 @@
|
|||
</ion-buttons>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<core-tabs [selectedIndex]="firstSelectedTab" [hideUntil]="tabsReady">
|
||||
<!-- Site home tab. -->
|
||||
<core-tab [show]="siteHomeEnabled" [title]="'core.sitehome.sitehome' | translate" (ionSelect)="tabChanged('sitehome')">
|
||||
<ng-template>
|
||||
<core-sitehome-index></core-sitehome-index>
|
||||
</ng-template>
|
||||
</core-tab>
|
||||
|
||||
<!-- Timeline tab. -->
|
||||
<core-tab [title]="'core.courses.timeline' | translate" (ionSelect)="tabChanged('timeline')">
|
||||
<ng-template>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="timeline.loaded || timelineCourses.loaded || courses.loaded" (ionRefresh)="refreshMyOverview($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<core-tabs selectedIndex="1">
|
||||
<!-- Timeline tab. -->
|
||||
<core-tab [title]="'core.courses.timeline' | translate" (ionSelect)="tabChanged('timeline')">
|
||||
<div no-padding [hidden]="!(timeline.loaded || timelineCourses.loaded)">
|
||||
<ion-select [(ngModel)]="timeline.sort" (ngModelChange)="switchSort()">
|
||||
<ion-select [(ngModel)]="timeline.sort" (ngModelChange)="switchSort()" interface="popover">
|
||||
<ion-option value="sortbydates">{{ 'core.courses.sortbydates' | translate }}</ion-option>
|
||||
<ion-option value="sortbycourses">{{ 'core.courses.sortbycourses' | translate }}</ion-option>
|
||||
</ion-select>
|
||||
|
@ -41,14 +50,22 @@
|
|||
</ion-grid>
|
||||
<core-empty-box *ngIf="timelineCourses.courses.length == 0" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursesoverview' | translate"></core-empty-box>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</core-tab>
|
||||
|
||||
<!-- Courses tab. -->
|
||||
<core-tab [title]="'core.courses.courses' | translate" (ionSelect)="tabChanged('courses')">
|
||||
<ng-template>
|
||||
<ion-content>
|
||||
<ion-refresher [enabled]="timeline.loaded || timelineCourses.loaded || courses.loaded" (ionRefresh)="refreshMyOverview($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<core-loading [hideUntil]="courses.loaded" class="core-loading-center">
|
||||
<!-- "Time" selector. -->
|
||||
<div no-padding class="clearfix" [hidden]="showFilter">
|
||||
<ion-select [title]="'core.show' | translate" [(ngModel)]="courses.selected" float-start (ngModelChange)="selectedChanged()">
|
||||
<ion-select [title]="'core.show' | translate" [(ngModel)]="courses.selected" float-start (ngModelChange)="selectedChanged()" interface="popover">
|
||||
<ion-option value="inprogress">{{ 'core.courses.inprogress' | translate }}</ion-option>
|
||||
<ion-option value="future">{{ 'core.courses.future' | translate }}</ion-option>
|
||||
<ion-option value="past">{{ 'core.courses.past' | translate }}</ion-option>
|
||||
|
@ -65,7 +82,7 @@
|
|||
<!-- Filter courses. -->
|
||||
<div no-padding padding-bottom [hidden]="!showFilter">
|
||||
<ion-item>
|
||||
<ion-label><ion-icon name="funnel" class="placeholder-icon"></ion-icon></ion-label>
|
||||
<ion-label item-start><ion-icon name="funnel" class="placeholder-icon"></ion-icon></ion-label>
|
||||
<ion-input type="text" name="filter" clearInput [(ngModel)]="courses.filter" (ngModelChange)="filterChanged($event)" [placeholder]="'core.courses.filtermycourses' | translate"></ion-input>
|
||||
</ion-item>
|
||||
</div>
|
||||
|
@ -84,7 +101,8 @@
|
|||
<core-empty-box *ngIf="courses[courses.selected].length == 0 && courses.selected == 'past'" image="assets/img/icons/courses.svg" [message]="'core.courses.nocoursespast' | translate"></core-empty-box>
|
||||
</div>
|
||||
</core-loading>
|
||||
</ion-content>
|
||||
</ng-template>
|
||||
</core-tab>
|
||||
</core-tabs>
|
||||
|
||||
</ion-content>
|
|
@ -18,6 +18,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
|||
import { CoreCoursesMyOverviewPage } from './my-overview';
|
||||
import { CoreComponentsModule } from '../../../../components/components.module';
|
||||
import { CoreCoursesComponentsModule } from '../../components/components.module';
|
||||
import { CoreSiteHomeComponentsModule } from '../../../sitehome/components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
|
@ -26,6 +27,7 @@ import { CoreCoursesComponentsModule } from '../../components/components.module'
|
|||
imports: [
|
||||
CoreComponentsModule,
|
||||
CoreCoursesComponentsModule,
|
||||
CoreSiteHomeComponentsModule,
|
||||
IonicPageModule.forChild(CoreCoursesMyOverviewPage),
|
||||
TranslateModule.forChild()
|
||||
],
|
||||
|
|
|
@ -14,10 +14,13 @@
|
|||
|
||||
import { Component, OnDestroy } from '@angular/core';
|
||||
import { IonicPage, NavController } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreCoursesProvider } from '../../providers/courses';
|
||||
import { CoreCoursesDelegate } from '../../providers/delegate';
|
||||
import { CoreCoursesMyOverviewProvider } from '../../providers/my-overview';
|
||||
import { CoreCourseHelperProvider } from '../../../course/providers/helper';
|
||||
import { CoreSiteHomeProvider } from '../../../sitehome/providers/sitehome';
|
||||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
|
@ -29,6 +32,9 @@ import * as moment from 'moment';
|
|||
templateUrl: 'my-overview.html',
|
||||
})
|
||||
export class CoreCoursesMyOverviewPage implements OnDestroy {
|
||||
firstSelectedTab: number;
|
||||
siteHomeEnabled: boolean;
|
||||
tabsReady: boolean = false;
|
||||
tabShown = 'courses';
|
||||
timeline = {
|
||||
sort: 'sortbydates',
|
||||
|
@ -64,13 +70,24 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
|
|||
|
||||
constructor(private navCtrl: NavController, private coursesProvider: CoreCoursesProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider,
|
||||
private courseHelper: CoreCourseHelperProvider) {}
|
||||
private courseHelper: CoreCourseHelperProvider, private sitesProvider: CoreSitesProvider,
|
||||
private siteHomeProvider: CoreSiteHomeProvider, private coursesDelegate: CoreCoursesDelegate) {}
|
||||
|
||||
/**
|
||||
* View loaded.
|
||||
*/
|
||||
ionViewDidLoad() {
|
||||
this.searchEnabled = !this.coursesProvider.isSearchCoursesDisabledInSite();
|
||||
|
||||
// Decide which tab to load first.
|
||||
this.siteHomeProvider.isAvailable().then((enabled) => {
|
||||
let site = this.sitesProvider.getCurrentSite(),
|
||||
displaySiteHome = site.getInfo() && site.getInfo().userhomepage === 0;
|
||||
|
||||
this.siteHomeEnabled = enabled;
|
||||
this.firstSelectedTab = displaySiteHome ? 0 : 2;
|
||||
this.tabsReady = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -222,7 +239,7 @@ export class CoreCoursesMyOverviewPage implements OnDestroy {
|
|||
}
|
||||
|
||||
promises.push(this.coursesProvider.invalidateUserCourses());
|
||||
// promises.push(this.coursesDelegate.clearAndInvalidateCoursesOptions());
|
||||
promises.push(this.coursesDelegate.clearAndInvalidateCoursesOptions());
|
||||
|
||||
return Promise.all(promises).finally(() => {
|
||||
switch (this.tabShown) {
|
||||
|
|
|
@ -0,0 +1,267 @@
|
|||
// (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 { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../providers/utils/dom';
|
||||
import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '../../contentlinks/providers/delegate';
|
||||
import { CoreLoginHelperProvider } from '../../login/providers/helper';
|
||||
import { CoreCourseProvider } from '../../course/providers/course';
|
||||
import { CoreCoursesProvider } from './courses';
|
||||
|
||||
/**
|
||||
* Handler to treat links to course view or enrol (except site home).
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreCoursesCourseLinkHandler extends CoreContentLinksHandlerBase {
|
||||
name = 'CoreCoursesCourseLinkHandler';
|
||||
pattern = /((\/enrol\/index\.php)|(\/course\/enrol\.php)|(\/course\/view\.php)).*([\?\&]id=\d+)/;
|
||||
|
||||
protected waitStart = 0;
|
||||
|
||||
constructor(private sitesProvider: CoreSitesProvider, private coursesProvider: CoreCoursesProvider,
|
||||
private loginHelper: CoreLoginHelperProvider, private domUtils: CoreDomUtilsProvider,
|
||||
private translate: TranslateService, private courseProvider: CoreCourseProvider) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
* @param {string[]} siteIds List of sites the URL belongs to.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||
*/
|
||||
getActions(siteIds: string[], url: string, params: any, courseId?: number) :
|
||||
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> {
|
||||
courseId = parseInt(params.id, 10);
|
||||
|
||||
let sectionId = params.sectionid ? parseInt(params.sectionid, 10) : null,
|
||||
sectionNumber = typeof params.section != 'undefined' ? parseInt(params.section, 10) : NaN,
|
||||
pageParams: any = {
|
||||
course: {id: courseId},
|
||||
sectionId: sectionId || null
|
||||
};
|
||||
|
||||
if (!isNaN(sectionNumber)) {
|
||||
pageParams.sectionNumber = sectionNumber;
|
||||
}
|
||||
|
||||
return [{
|
||||
action: (siteId, navCtrl?) => {
|
||||
siteId = siteId || this.sitesProvider.getCurrentSiteId();
|
||||
if (siteId == this.sitesProvider.getCurrentSiteId()) {
|
||||
this.actionEnrol(courseId, url, pageParams).catch(() => {
|
||||
// Ignore errors.
|
||||
});
|
||||
} else {
|
||||
// Use redirect to make the course the new history root (to avoid "loops" in history).
|
||||
this.loginHelper.redirect('CoreCourseSectionPage', pageParams, siteId);
|
||||
}
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled for a certain site (site + user) and a URL.
|
||||
* If not defined, defaults to true.
|
||||
*
|
||||
* @param {string} siteId The site ID.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site.
|
||||
*/
|
||||
isEnabled(siteId: string, url: string, params: any, courseId?: number) : boolean|Promise<boolean> {
|
||||
courseId = parseInt(params.id, 10);
|
||||
|
||||
if (!courseId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the course id of Site Home.
|
||||
return this.sitesProvider.getSiteHomeId(siteId).then((siteHomeId) => {
|
||||
return courseId != siteHomeId;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Action to perform when an enrol link is clicked.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} url Treated URL.
|
||||
* @param {any} pageParams Params to send to the new page.
|
||||
* @return {Promise<any>} Promise resolved when done.
|
||||
*/
|
||||
protected actionEnrol(courseId: number, url: string, pageParams: any) : Promise<any> {
|
||||
let modal = this.domUtils.showModalLoading(),
|
||||
isEnrolUrl = !!url.match(/(\/enrol\/index\.php)|(\/course\/enrol\.php)/);
|
||||
|
||||
// Check if user is enrolled in the course.
|
||||
return this.coursesProvider.getUserCourse(courseId).catch(() => {
|
||||
// User is not enrolled in the course. Check if can self enrol.
|
||||
return this.canSelfEnrol(courseId).then(() => {
|
||||
modal.dismiss();
|
||||
|
||||
// The user can self enrol. If it's not a enrolment URL we'll ask for confirmation.
|
||||
let promise = isEnrolUrl ? Promise.resolve() :
|
||||
this.domUtils.showConfirm(this.translate.instant('core.courses.confirmselfenrol'));
|
||||
|
||||
return promise.then(() => {
|
||||
// Enrol URL or user confirmed.
|
||||
return this.selfEnrol(courseId).catch((error) => {
|
||||
if (error) {
|
||||
this.domUtils.showErrorModal(error);
|
||||
}
|
||||
return Promise.reject(null);
|
||||
});
|
||||
}, () => {
|
||||
// User cancelled. Check if the user can view the course contents (guest access or similar).
|
||||
return this.courseProvider.getSections(courseId, false, true);
|
||||
});
|
||||
}, (error) => {
|
||||
// Can't self enrol. Check if the user can view the course contents (guest access or similar).
|
||||
return this.courseProvider.getSections(courseId, false, true).catch(() => {
|
||||
// Error. Show error message and allow the user to open the link in browser.
|
||||
modal.dismiss();
|
||||
|
||||
if (error) {
|
||||
error = error.message || error.error || error.content || error.body || error;
|
||||
}
|
||||
if (!error) {
|
||||
error = this.translate.instant('core.courses.notenroled');
|
||||
}
|
||||
|
||||
let body = this.translate.instant('core.twoparagraphs',
|
||||
{p1: error, p2: this.translate.instant('core.confirmopeninbrowser')});
|
||||
this.domUtils.showConfirm(body).then(() => {
|
||||
this.sitesProvider.getCurrentSite().openInBrowserWithAutoLogin(url);
|
||||
}).catch(() => {
|
||||
// User cancelled.
|
||||
});
|
||||
return Promise.reject(null);
|
||||
});
|
||||
});
|
||||
}).then(() => {
|
||||
modal.dismiss();
|
||||
|
||||
// Use redirect to make the course the new history root (to avoid "loops" in history).
|
||||
this.loginHelper.redirect('CoreCourseSectionPage', pageParams, this.sitesProvider.getCurrentSiteId());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a user can be "automatically" self enrolled in a course.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @return {Promise<any>} Promise resolved if user can be enrolled in a course, rejected otherwise.
|
||||
*/
|
||||
protected canSelfEnrol(courseId: number) : Promise<any> {
|
||||
// Check that the course has self enrolment enabled.
|
||||
return this.coursesProvider.getCourseEnrolmentMethods(courseId).then((methods) => {
|
||||
let isSelfEnrolEnabled = false,
|
||||
instances = 0;
|
||||
|
||||
methods.forEach((method) => {
|
||||
if (method.type == 'self' && method.status) {
|
||||
isSelfEnrolEnabled = true;
|
||||
instances++;
|
||||
}
|
||||
});
|
||||
|
||||
if (!isSelfEnrolEnabled || instances != 1) {
|
||||
// Self enrol not enabled or more than one instance.
|
||||
return Promise.reject(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to self enrol a user in a course.
|
||||
*
|
||||
* @param {number} courseId Course ID.
|
||||
* @param {string} [password] Password.
|
||||
* @return {Promise<any>} Promise resolved when the user is enrolled, rejected otherwise.
|
||||
*/
|
||||
protected selfEnrol(courseId: number, password?: string) : Promise<any> {
|
||||
const modal = this.domUtils.showModalLoading();
|
||||
return this.coursesProvider.selfEnrol(courseId, password).then(() => {
|
||||
// Success self enrolling the user, invalidate the courses list.
|
||||
return this.coursesProvider.invalidateUserCourses().catch(() => {
|
||||
// Ignore errors.
|
||||
}).then(() => {
|
||||
// Sometimes the list of enrolled courses takes a while to be updated. Wait for it.
|
||||
return this.waitForEnrolled(courseId, true).finally(() => {
|
||||
modal.dismiss();
|
||||
});
|
||||
});
|
||||
|
||||
}).catch((error) => {
|
||||
modal.dismiss();
|
||||
if (error && error.code === CoreCoursesProvider.ENROL_INVALID_KEY) {
|
||||
// Invalid password. Allow the user to input password.
|
||||
let title = this.translate.instant('core.courses.selfenrolment'),
|
||||
body = ' ', // Empty message.
|
||||
placeholder = this.translate.instant('core.courses.password');
|
||||
|
||||
if (typeof password != 'undefined') {
|
||||
// The user attempted a password. Show an error message.
|
||||
this.domUtils.showErrorModal(error.message);
|
||||
}
|
||||
|
||||
return this.domUtils.showPrompt(body, title, placeholder).then((password) => {
|
||||
return this.selfEnrol(courseId, password);
|
||||
});
|
||||
} else {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the user to be enrolled in a course.
|
||||
*
|
||||
* @param {number} courseId The course ID.
|
||||
* @param {boolean} first If it's the first call (true) or it's a recursive call (false).
|
||||
* @return {Promise<any>} Promise resolved when enrolled or timeout.
|
||||
*/
|
||||
protected waitForEnrolled(courseId: number, first?: boolean) : Promise<any> {
|
||||
if (first) {
|
||||
this.waitStart = Date.now();
|
||||
}
|
||||
|
||||
// Check if user is enrolled in the course.
|
||||
return this.coursesProvider.invalidateUserCourses().catch(() => {
|
||||
// Ignore errors.
|
||||
}).then(() => {
|
||||
return this.coursesProvider.getUserCourse(courseId);
|
||||
}).catch(() => {
|
||||
// Not enrolled, wait a bit and try again.
|
||||
if (Date.now() - this.waitStart > 60000) {
|
||||
// Max time reached, stop.
|
||||
return;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => {
|
||||
this.waitForEnrolled(courseId).then(resolve);
|
||||
}, 5000);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
// (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 { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '../../contentlinks/providers/delegate';
|
||||
import { CoreLoginHelperProvider } from '../../login/providers/helper';
|
||||
import { CoreCoursesProvider } from './courses';
|
||||
|
||||
/**
|
||||
* Handler to treat links to course index (list of courses).
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreCoursesIndexLinkHandler extends CoreContentLinksHandlerBase {
|
||||
name = 'CoreCoursesIndexLinkHandler';
|
||||
featureName = '$mmSideMenuDelegate_mmCourses';
|
||||
pattern = /\/course\/?(index\.php.*)?$/;
|
||||
|
||||
constructor(private coursesProvider: CoreCoursesProvider, private loginHelper: CoreLoginHelperProvider) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
* @param {string[]} siteIds List of sites the URL belongs to.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||
*/
|
||||
getActions(siteIds: string[], url: string, params: any, courseId?: number) :
|
||||
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> {
|
||||
return [{
|
||||
action: (siteId, navCtrl?) => {
|
||||
var page = 'CoreCoursesMyCoursesPage', // By default, go to My Courses.
|
||||
pageParams: any = {};
|
||||
|
||||
if (this.coursesProvider.isGetCoursesByFieldAvailable()) {
|
||||
if (params.categoryid) {
|
||||
page = 'CoreCoursesCategoriesPage';
|
||||
pageParams.categoryId = parseInt(params.categoryid, 10);
|
||||
} else {
|
||||
page = 'CoreCoursesAvailableCoursesPage';
|
||||
}
|
||||
}
|
||||
|
||||
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||
this.loginHelper.redirect(page, pageParams, siteId);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '../../mainmenu/pro
|
|||
import { CoreCoursesMyOverviewProvider } from '../providers/my-overview';
|
||||
|
||||
/**
|
||||
* Handler to inject an option into main menu.
|
||||
* Handler to add My Courses or My Overview into main menu.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreCoursesMainMenuHandler implements CoreMainMenuHandler {
|
|
@ -0,0 +1,52 @@
|
|||
// (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 { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '../../contentlinks/providers/delegate';
|
||||
import { CoreLoginHelperProvider } from '../../login/providers/helper';
|
||||
import { CoreCoursesProvider } from './courses';
|
||||
|
||||
/**
|
||||
* Handler to treat links to my overview.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreCoursesMyOverviewLinkHandler extends CoreContentLinksHandlerBase {
|
||||
name = 'CoreCoursesMyOverviewLinkHandler';
|
||||
featureName = '$mmSideMenuDelegate_mmCourses';
|
||||
pattern = /\/my\/?$/;
|
||||
|
||||
constructor(private coursesProvider: CoreCoursesProvider, private loginHelper: CoreLoginHelperProvider) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
* @param {string[]} siteIds List of sites the URL belongs to.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||
*/
|
||||
getActions(siteIds: string[], url: string, params: any, courseId?: number) :
|
||||
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> {
|
||||
return [{
|
||||
action: (siteId, navCtrl?) => {
|
||||
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||
this.loginHelper.redirect('CoreCoursesMyOverviewPage', undefined, siteId);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
|
@ -21,6 +21,8 @@ import { CoreSitesProvider } from '../../../../providers/sites';
|
|||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreUtilsProvider } from '../../../../providers/utils/utils';
|
||||
import { CoreLoginHelperProvider } from '../../providers/helper';
|
||||
import { CoreContentLinksDelegate } from '../../../contentlinks/providers/delegate';
|
||||
import { CoreContentLinksHelperProvider } from '../../../contentlinks/providers/helper';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
|
||||
/**
|
||||
|
@ -52,7 +54,8 @@ export class CoreLoginCredentialsPage {
|
|||
constructor(private navCtrl: NavController, navParams: NavParams, fb: FormBuilder, private appProvider: CoreAppProvider,
|
||||
private sitesProvider: CoreSitesProvider, private loginHelper: CoreLoginHelperProvider,
|
||||
private domUtils: CoreDomUtilsProvider, private translate: TranslateService, private utils: CoreUtilsProvider,
|
||||
private eventsProvider: CoreEventsProvider) {
|
||||
private eventsProvider: CoreEventsProvider, private contentLinksDelegate: CoreContentLinksDelegate,
|
||||
private contentLinksHelper: CoreContentLinksHelperProvider) {
|
||||
|
||||
this.siteUrl = navParams.get('siteUrl');
|
||||
this.siteConfig = navParams.get('siteConfig');
|
||||
|
@ -203,16 +206,15 @@ export class CoreLoginCredentialsPage {
|
|||
|
||||
if (this.urlToOpen) {
|
||||
// There's a content link to open.
|
||||
// @todo: Implement this once content links delegate is implemented.
|
||||
// return $mmContentLinksDelegate.getActionsFor(urlToOpen, undefined, username).then((actions) => {
|
||||
// action = $mmContentLinksHelper.getFirstValidAction(actions);
|
||||
// if (action && action.sites.length) {
|
||||
// // Action should only have 1 site because we're filtering by username.
|
||||
// action.action(action.sites[0]);
|
||||
// } else {
|
||||
// return $mmLoginHelper.goToSiteInitialPage();
|
||||
// }
|
||||
// });
|
||||
return this.contentLinksDelegate.getActionsFor(this.urlToOpen, undefined, username).then((actions) => {
|
||||
const action = this.contentLinksHelper.getFirstValidAction(actions);
|
||||
if (action && action.sites.length) {
|
||||
// Action should only have 1 site because we're filtering by username.
|
||||
action.action(action.sites[0]);
|
||||
} else {
|
||||
return this.loginHelper.goToSiteInitialPage();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
return this.loginHelper.goToSiteInitialPage();
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@
|
|||
</ion-item>
|
||||
<ion-item text-wrap>
|
||||
<ion-label stacked id="core-login-signup-country">{{ 'core.user.country' | translate }}</ion-label>
|
||||
<ion-select name="country" formControlName="country" aria-labelledby="core-login-signup-country">
|
||||
<ion-select name="country" formControlName="country" aria-labelledby="core-login-signup-country" interface="popover">
|
||||
<ion-option value="">{{ 'core.login.selectacountry' | translate }}</ion-option>
|
||||
<ion-option *ngFor="let key of countriesKeys" [value]="key">{{countries[key]}}</ion-option>
|
||||
</ion-select>
|
||||
|
|
|
@ -29,7 +29,7 @@
|
|||
<ion-item *ngIf="!displayAsButtons">
|
||||
<!-- @todo: Display label and select in different lines. -->
|
||||
<ion-label for="siteSelect">{{ 'core.login.selectsite' | translate }}</ion-label>
|
||||
<ion-select formControlName="siteUrl" name="url" placeholder="{{ 'core.login.siteaddress' | translate }}">
|
||||
<ion-select formControlName="siteUrl" name="url" placeholder="{{ 'core.login.siteaddress' | translate }}" interface="popover">
|
||||
<ion-option *ngFor="let site of fixedSites" [value]="site.url">{{site.name}}</ion-option>
|
||||
</ion-select>
|
||||
</ion-item>
|
||||
|
|
|
@ -404,49 +404,6 @@ export class CoreLoginHelperProvider {
|
|||
*/
|
||||
goToSiteInitialPage() : Promise<any> {
|
||||
return this.appProvider.getRootNavController().setRoot('CoreMainMenuPage');
|
||||
// return this.isMyOverviewEnabled().then((myOverview) => {
|
||||
// let myCourses = !myOverview && this.isMyCoursesEnabled(),
|
||||
// site = this.sitesProvider.getCurrentSite(),
|
||||
// promise;
|
||||
|
||||
// if (!site) {
|
||||
// return Promise.reject(null);
|
||||
// }
|
||||
|
||||
// // Check if frontpage is needed to be shown. (If configured or if any of the other avalaible).
|
||||
// if ((site.getInfo() && site.getInfo().userhomepage === 0) || (!myCourses && !myOverview)) {
|
||||
// promise = this.isFrontpageEnabled();
|
||||
// } else {
|
||||
// promise = Promise.resolve(false);
|
||||
// }
|
||||
|
||||
// return promise.then((frontpage) => {
|
||||
// // Check avalaibility in priority order.
|
||||
// let pageName,
|
||||
// params;
|
||||
|
||||
// // @todo Use real pages names when they are implemented.
|
||||
// if (frontpage) {
|
||||
// pageName = 'Frontpage';
|
||||
// } else if (myOverview) {
|
||||
// pageName = 'MyOverview';
|
||||
// } else if (myCourses) {
|
||||
// pageName = 'MyCourses';
|
||||
// } else {
|
||||
// // Anything else available, go to the user profile.
|
||||
// pageName = 'User';
|
||||
// params = {
|
||||
// userId: site.getUserId()
|
||||
// };
|
||||
// }
|
||||
|
||||
// if (setRoot) {
|
||||
// return navCtrl.setRoot(pageName, params, {animate: false});
|
||||
// } else {
|
||||
// return navCtrl.push(pageName, params);
|
||||
// }
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -547,45 +504,6 @@ export class CoreLoginHelperProvider {
|
|||
return !!CoreConfigConstants.siteurl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the app is configured to use a fixed URL (only 1).
|
||||
*
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: whether there is 1 fixed URL.
|
||||
*/
|
||||
protected isFrontpageEnabled() : Promise<boolean> {
|
||||
// var $mmaFrontpage = $mmAddonManager.get('$mmaFrontpage');
|
||||
// if ($mmaFrontpage && !$mmaFrontpage.isDisabledInSite()) {
|
||||
// return $mmaFrontpage.isFrontpageAvailable().then(() => {
|
||||
// return true;
|
||||
// }).catch(() => {
|
||||
// return false;
|
||||
// });
|
||||
// }
|
||||
// @todo: Implement it when front page is implemented.
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if My Courses is enabled.
|
||||
*
|
||||
* @return {boolean} Whether My Courses is enabled.
|
||||
*/
|
||||
protected isMyCoursesEnabled() : boolean {
|
||||
// @todo: Implement it when My Courses is implemented.
|
||||
return false;
|
||||
// return !$mmCourses.isMyCoursesDisabledInSite();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if My Overview is enabled.
|
||||
*
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: whether My Overview is enabled.
|
||||
*/
|
||||
protected isMyOverviewEnabled() : Promise<boolean> {
|
||||
// @todo: Implement it when My Overview is implemented.
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if current site is logged out, triggering mmCoreEventSessionExpired if it is.
|
||||
*
|
||||
|
@ -858,7 +776,7 @@ export class CoreLoginHelperProvider {
|
|||
if (siteId) {
|
||||
this.loadSiteAndPage(page, params, siteId);
|
||||
} else {
|
||||
this.appProvider.getRootNavController().setRoot('CoreLoginSitesPage')
|
||||
this.appProvider.getRootNavController().setRoot('CoreLoginSitesPage');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<ion-tabs *ngIf="loaded" #mainTabs>
|
||||
<ion-tabs *ngIf="loaded" #mainTabs [selectedIndex]="initialTab">
|
||||
<ion-tab [enabled]="false" [show]="false" [root]="redirectPage" [rootParams]="redirectParams"></ion-tab>
|
||||
<ion-tab *ngFor="let tab of tabs" [root]="tab.page" [tabTitle]="tab.title | translate" [tabIcon]="tab.icon" class="{{tab.class}}"></ion-tab>
|
||||
</ion-tabs>
|
|
@ -54,6 +54,7 @@ export class CoreMainMenuPage implements OnDestroy {
|
|||
loaded: boolean;
|
||||
redirectPage: string;
|
||||
redirectParams: any;
|
||||
initialTab: number;
|
||||
|
||||
protected subscription;
|
||||
protected moreTabData = {
|
||||
|
@ -79,25 +80,36 @@ export class CoreMainMenuPage implements OnDestroy {
|
|||
return;
|
||||
}
|
||||
|
||||
let 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.
|
||||
|
||||
// Check if handlers are already in tabs. Add the ones that aren't.
|
||||
// @todo: https://github.com/ionic-team/ionic/issues/13633
|
||||
for (let i in handlers) {
|
||||
for (let i = 0; i < handlers.length; i++) {
|
||||
let handler = handlers[i],
|
||||
found = false;
|
||||
found = false,
|
||||
shouldSelect = (displaySiteHome && handler.name == 'CoreSiteHome') ||
|
||||
(!displaySiteHome && handler.name == 'CoreCourses');
|
||||
|
||||
for (let j in this.tabs) {
|
||||
for (let j = 0; j < this.tabs.length; j++) {
|
||||
let tab = this.tabs[j];
|
||||
if (tab.title == handler.title && tab.icon == handler.icon) {
|
||||
found = true;
|
||||
if (shouldSelect) {
|
||||
this.initialTab = j;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
this.tabs.push(handler);
|
||||
if (shouldSelect) {
|
||||
this.initialTab = this.tabs.length;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -79,6 +79,18 @@ export interface CoreMainMenuHandlerData {
|
|||
class?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Data returned by the delegate for each handler.
|
||||
*/
|
||||
export interface CoreMainMenuHandlerToDisplay extends CoreMainMenuHandlerData {
|
||||
|
||||
/**
|
||||
* Name of the handler.
|
||||
* @type {string}
|
||||
*/
|
||||
name?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Service to interact with plugins to be shown in the main menu. Provides functions to register a plugin
|
||||
* and notify an update in the data.
|
||||
|
@ -90,7 +102,7 @@ export class CoreMainMenuDelegate {
|
|||
protected enabledHandlers: {[s: string]: CoreMainMenuHandler} = {};
|
||||
protected loaded = false;
|
||||
protected lastUpdateHandlersStart: number;
|
||||
protected siteHandlers: Subject<CoreMainMenuHandlerData[]> = new BehaviorSubject<CoreMainMenuHandlerData[]>([]);
|
||||
protected siteHandlers: Subject<CoreMainMenuHandlerToDisplay[]> = new BehaviorSubject<CoreMainMenuHandlerToDisplay[]>([]);
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider) {
|
||||
this.logger = logger.getInstance('CoreMainMenuDelegate');
|
||||
|
@ -123,7 +135,7 @@ export class CoreMainMenuDelegate {
|
|||
*
|
||||
* @return {Subject<CoreMainMenuHandlerData[]>} An observable that will receive the handlers.
|
||||
*/
|
||||
getHandlers() : Subject<CoreMainMenuHandlerData[]> {
|
||||
getHandlers() : Subject<CoreMainMenuHandlerToDisplay[]> {
|
||||
return this.siteHandlers;
|
||||
}
|
||||
|
||||
|
@ -223,7 +235,9 @@ export class CoreMainMenuDelegate {
|
|||
|
||||
for (let name in this.enabledHandlers) {
|
||||
let handler = this.enabledHandlers[name],
|
||||
data = handler.getDisplayData();
|
||||
data: CoreMainMenuHandlerToDisplay = handler.getDisplayData();
|
||||
|
||||
data.name = handler.name;
|
||||
|
||||
handlersData.push({
|
||||
data: data,
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
<a *ngIf="show" ion-item text-wrap [navPush]="'CoreCoursesAvailableCoursesPage'">
|
||||
<ion-icon name="ionic" item-start></ion-icon>
|
||||
<h2>{{ 'core.courses.availablecourses' | translate}}</h2>
|
||||
</a>
|
|
@ -0,0 +1,32 @@
|
|||
// (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, Input } from '@angular/core';
|
||||
import { IonicPage } from 'ionic-angular';
|
||||
import { CoreCoursesProvider } from '../../../courses/providers/courses';
|
||||
|
||||
/**
|
||||
* Component to open the page to view the list of all courses.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-sitehome-all-course-list',
|
||||
templateUrl: 'all-course-list.html',
|
||||
})
|
||||
export class CoreSiteHomeAllCourseListComponent {
|
||||
show: boolean;
|
||||
|
||||
constructor(coursesProvider: CoreCoursesProvider) {
|
||||
this.show = coursesProvider.isGetCoursesByFieldAvailable();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<a *ngIf="show" ion-item text-wrap [navPush]="'CoreCoursesCategoriesPage'">
|
||||
<ion-icon name="folder" item-start></ion-icon>
|
||||
<h2>{{ 'core.courses.categories' | translate}}</h2>
|
||||
</a>
|
|
@ -0,0 +1,32 @@
|
|||
// (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, Input } from '@angular/core';
|
||||
import { IonicPage } from 'ionic-angular';
|
||||
import { CoreCoursesProvider } from '../../../courses/providers/courses';
|
||||
|
||||
/**
|
||||
* Component to open the page to view the list of categories.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-sitehome-categories',
|
||||
templateUrl: 'categories.html',
|
||||
})
|
||||
export class CoreSiteHomeCategoriesComponent {
|
||||
show: boolean;
|
||||
|
||||
constructor(coursesProvider: CoreCoursesProvider) {
|
||||
this.show = coursesProvider.isGetCoursesByFieldAvailable();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
// (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 { CoreCourseComponentsModule } from '../../course/components/components.module';
|
||||
import { CoreSiteHomeIndexComponent } from './index/index';
|
||||
import { CoreSiteHomeAllCourseListComponent } from './all-course-list/all-course-list';
|
||||
import { CoreSiteHomeCategoriesComponent } from './categories/categories';
|
||||
import { CoreSiteHomeCourseSearchComponent } from './course-search/course-search';
|
||||
import { CoreSiteHomeEnrolledCourseListComponent } from './enrolled-course-list/enrolled-course-list';
|
||||
import { CoreSiteHomeNewsComponent } from './news/news';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreSiteHomeIndexComponent,
|
||||
CoreSiteHomeAllCourseListComponent,
|
||||
CoreSiteHomeCategoriesComponent,
|
||||
CoreSiteHomeCourseSearchComponent,
|
||||
CoreSiteHomeEnrolledCourseListComponent,
|
||||
CoreSiteHomeNewsComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreComponentsModule,
|
||||
CoreDirectivesModule,
|
||||
CoreCourseComponentsModule
|
||||
],
|
||||
exports: [
|
||||
CoreSiteHomeIndexComponent,
|
||||
CoreSiteHomeAllCourseListComponent,
|
||||
CoreSiteHomeCategoriesComponent,
|
||||
CoreSiteHomeCourseSearchComponent,
|
||||
CoreSiteHomeEnrolledCourseListComponent,
|
||||
CoreSiteHomeNewsComponent
|
||||
]
|
||||
})
|
||||
export class CoreSiteHomeComponentsModule {}
|
|
@ -0,0 +1,4 @@
|
|||
<a *ngIf="show" ion-item text-wrap [navPush]="'CoreCoursesSearchPage'">
|
||||
<ion-icon name="search" item-start></ion-icon>
|
||||
<h2>{{ 'core.courses.searchcourses' | translate}}</h2>
|
||||
</a>
|
|
@ -0,0 +1,32 @@
|
|||
// (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, Input } from '@angular/core';
|
||||
import { IonicPage } from 'ionic-angular';
|
||||
import { CoreCoursesProvider } from '../../../courses/providers/courses';
|
||||
|
||||
/**
|
||||
* Component to open the page to search courses.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-sitehome-course-search',
|
||||
templateUrl: 'course-search.html',
|
||||
})
|
||||
export class CoreSiteHomeCourseSearchComponent {
|
||||
show: boolean;
|
||||
|
||||
constructor(coursesProvider: CoreCoursesProvider) {
|
||||
this.show = !coursesProvider.isSearchCoursesDisabledInSite();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<a *ngIf="show" ion-item text-wrap [navPush]="'CoreCoursesMyCoursesPage'">
|
||||
<ion-icon name="ionic" item-start></ion-icon>
|
||||
<h2>{{ 'core.courses.mycourses' | translate}}</h2>
|
||||
</a>
|
|
@ -0,0 +1,43 @@
|
|||
// (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, Input, OnInit } from '@angular/core';
|
||||
import { IonicPage } from 'ionic-angular';
|
||||
import { CoreCoursesProvider } from '../../../courses/providers/courses';
|
||||
|
||||
/**
|
||||
* Component to open the page to view the list of courses the user is enrolled in.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-sitehome-enrolled-course-list',
|
||||
templateUrl: 'enrolled-course-list.html',
|
||||
})
|
||||
export class CoreSiteHomeEnrolledCourseListComponent implements OnInit {
|
||||
show: boolean;
|
||||
|
||||
constructor(private coursesProvider: CoreCoursesProvider) {}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit() {
|
||||
if (this.coursesProvider.isMyCoursesDisabledInSite()) {
|
||||
this.show = false;
|
||||
} else {
|
||||
return this.coursesProvider.getUserCourses().then((courses) => {
|
||||
this.show = courses.length > 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<ion-content>
|
||||
<ion-refresher [enabled]="dataLoaded" (ionRefresh)="doRefresh($event)">
|
||||
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||
</ion-refresher>
|
||||
|
||||
<core-loading [hideUntil]="dataLoaded">
|
||||
|
||||
<ion-list>
|
||||
<!-- Site home main contents. -->
|
||||
<ng-container *ngIf="section && section.hasContent">
|
||||
<ion-item text-wrap *ngIf="section.summary">
|
||||
<core-format-text [text]="section.summary"></core-format-text>
|
||||
</ion-item>
|
||||
|
||||
<core-course-module *ngFor="let module of section.modules" [module]="module" [courseId]="siteHomeId"></core-course-module>
|
||||
</ng-container>
|
||||
|
||||
<!-- Site home items: news, categories, courses, etc. -->
|
||||
<ng-container *ngIf="items.length > 0">
|
||||
<ion-item-divider color="light" *ngIf="section && section.hasContent"></ion-item-divider>
|
||||
<ng-container *ngFor="let item of items">
|
||||
<core-sitehome-all-course-list class="item" *ngIf="item == 'all-course-list'"></core-sitehome-all-course-list>
|
||||
<core-sitehome-categories *ngIf="item == 'categories'"></core-sitehome-categories>
|
||||
<core-sitehome-course-search *ngIf="item == 'course-search'"></core-sitehome-course-search>
|
||||
<core-sitehome-enrolled-course-list *ngIf="item == 'enrolled-course-list'"></core-sitehome-enrolled-course-list>
|
||||
<core-sitehome-news *ngIf="item == 'news'"></core-sitehome-news>
|
||||
</ng-container>
|
||||
</ng-container>
|
||||
|
||||
<!-- Site home block. -->
|
||||
<ng-container *ngIf="block && block.hasContent">
|
||||
<ion-item-divider color="light" *ngIf="(section && section.hasContent) || items.length > 0"></ion-item-divider>
|
||||
<ion-item text-wrap *ngIf="block.summary">
|
||||
<core-format-text [text]="block.summary"></core-format-text>
|
||||
</ion-item>
|
||||
|
||||
<core-course-module *ngFor="let module of block.modules" [module]="module" [courseId]="siteHomeId"></core-course-module>
|
||||
</ng-container>
|
||||
</ion-list>
|
||||
|
||||
<core-empty-box *ngIf="!hasContent" icon="qr-scanner" [message]="'core.course.nocontentavailable' | translate"></core-empty-box>
|
||||
</core-loading>
|
||||
</ion-content>
|
|
@ -0,0 +1,143 @@
|
|||
// (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, OnInit, Input } from '@angular/core';
|
||||
import { IonicPage } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../../../../providers/utils/dom';
|
||||
import { CoreCourseProvider } from '../../../course/providers/course';
|
||||
import { CoreCourseHelperProvider } from '../../../course/providers/helper';
|
||||
import { CoreCourseModulePrefetchDelegate } from '../../../course/providers/module-prefetch-delegate';
|
||||
|
||||
/**
|
||||
* Component that displays site home index.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-sitehome-index',
|
||||
templateUrl: 'index.html',
|
||||
})
|
||||
export class CoreSiteHomeIndexComponent implements OnInit {
|
||||
dataLoaded: boolean;
|
||||
section: any;
|
||||
block: any;
|
||||
hasContent: boolean;
|
||||
items: any[] = [];
|
||||
siteHomeId: number;
|
||||
|
||||
protected sectionsLoaded: any[];
|
||||
|
||||
constructor(private domUtils: CoreDomUtilsProvider, private sitesProvider: CoreSitesProvider,
|
||||
private courseProvider: CoreCourseProvider, private courseHelper: CoreCourseHelperProvider,
|
||||
private prefetchDelegate: CoreCourseModulePrefetchDelegate) {
|
||||
this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit() {
|
||||
this.loadContent().finally(() => {
|
||||
this.dataLoaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the data.
|
||||
*
|
||||
* @param {any} refresher Refresher.
|
||||
*/
|
||||
doRefresh(refresher: any) {
|
||||
const promises = [],
|
||||
currentSite = this.sitesProvider.getCurrentSite();
|
||||
|
||||
promises.push(this.courseProvider.invalidateSections(this.siteHomeId));
|
||||
promises.push(currentSite.invalidateConfig().then(() => {
|
||||
// Config invalidated, fetch it again.
|
||||
return currentSite.getConfig().then((config) => {
|
||||
currentSite.setConfig(config);
|
||||
});
|
||||
}));
|
||||
|
||||
if (this.sectionsLoaded) {
|
||||
// Invalidate modules prefetch data.
|
||||
const modules = this.courseProvider.getSectionsModules(this.sectionsLoaded);
|
||||
promises.push(this.prefetchDelegate.invalidateModules(modules, this.siteHomeId));
|
||||
}
|
||||
|
||||
Promise.all(promises).finally(() => {
|
||||
this.loadContent().finally(() => {
|
||||
refresher.complete();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience function to fetch the data.
|
||||
*/
|
||||
protected loadContent() {
|
||||
this.hasContent = false;
|
||||
|
||||
let config = this.sitesProvider.getCurrentSite().getStoredConfig() || {numsections: 1};
|
||||
|
||||
if (config.frontpageloggedin) {
|
||||
// Items with index 1 and 3 were removed on 2.5 and not being supported in the app.
|
||||
let frontpageItems = [
|
||||
'news', // News items.
|
||||
false,
|
||||
'categories', // List of categories.
|
||||
false,
|
||||
'categories', // Combo list.
|
||||
'enrolled-course-list', // Enrolled courses.
|
||||
'all-course-list', // List of courses.
|
||||
'course-search' // Course search box.
|
||||
],
|
||||
items = config.frontpageloggedin.split(',');
|
||||
|
||||
this.items = [];
|
||||
|
||||
items.forEach((itemNumber) => {
|
||||
// Get the frontpage item "name".
|
||||
const item = frontpageItems[parseInt(itemNumber, 10)];
|
||||
if (!item || this.items.indexOf(item) >= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.hasContent = true;
|
||||
this.items.push(item);
|
||||
});
|
||||
}
|
||||
|
||||
return this.courseProvider.getSections(this.siteHomeId, false, true).then((sections) => {
|
||||
this.sectionsLoaded = Array.from(sections);
|
||||
|
||||
// Check "Include a topic section" setting from numsections.
|
||||
this.section = config.numsections && sections.length > 0 ? sections.pop() : false;
|
||||
if (this.section) {
|
||||
this.section.hasContent = this.courseHelper.sectionHasContent(this.section);
|
||||
}
|
||||
|
||||
this.block = sections.length > 0 ? sections.pop() : false;
|
||||
if (this.block) {
|
||||
this.block.hasContent = this.courseHelper.sectionHasContent(this.block);
|
||||
}
|
||||
|
||||
this.hasContent = this.courseHelper.addHandlerDataForModules(this.sectionsLoaded, this.siteHomeId) || this.hasContent;
|
||||
|
||||
// Add log in Moodle.
|
||||
this.courseProvider.logView(this.siteHomeId);
|
||||
}).catch((error) => {
|
||||
this.domUtils.showErrorModalDefault(error, 'core.course.couldnotloadsectioncontent', true);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<core-course-module class="core-sitehome-news" *ngIf="show" [module]="module" [courseId]="siteHomeId"></core-course-module>
|
|
@ -0,0 +1,67 @@
|
|||
// (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, OnInit, Input } from '@angular/core';
|
||||
import { IonicPage } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
|
||||
/**
|
||||
* Component that displays site home news.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'core-sitehome-news',
|
||||
templateUrl: 'news.html',
|
||||
})
|
||||
export class CoreSiteHomeNewsComponent implements OnInit {
|
||||
module: any;
|
||||
show: boolean;
|
||||
siteHomeId: number;
|
||||
|
||||
constructor(private sitesProvider: CoreSitesProvider) {
|
||||
this.siteHomeId = sitesProvider.getCurrentSite().getSiteHomeId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Component being initialized.
|
||||
*/
|
||||
ngOnInit() {
|
||||
// Get number of news items to show.
|
||||
const newsItems = this.sitesProvider.getCurrentSite().getStoredConfig('newsitems') || 0;
|
||||
if (!newsItems) {
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo: Implement it once forum is supported.
|
||||
// $mmaModForum = $mmAddonManager.get('$mmaModForum');
|
||||
// if ($mmaModForum) {
|
||||
// return $mmaModForum.getCourseForums(courseId).then(function(forums) {
|
||||
// for (var x in forums) {
|
||||
// if (forums[x].type == 'news') {
|
||||
// return forums[x];
|
||||
// }
|
||||
// }
|
||||
// }).then(function(forum) {
|
||||
// if (forum) {
|
||||
// return $mmCourse.getModuleBasicInfo(forum.cmid).then(function(module) {
|
||||
// scope.show = true;
|
||||
// scope.module = module;
|
||||
// scope.module._controller =
|
||||
// $mmCourseDelegate.getContentHandlerControllerFor(module.modname, module, courseId,
|
||||
// module.section);
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"sitehome": "Site home",
|
||||
"sitenews": "Site announcements"
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<ion-header>
|
||||
<ion-navbar>
|
||||
<ion-title>{{ 'core.sitehome.sitehome' | translate }}</ion-title>
|
||||
</ion-navbar>
|
||||
</ion-header>
|
||||
<core-sitehome-index></core-sitehome-index>
|
|
@ -0,0 +1,31 @@
|
|||
// (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 { IonicPageModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreSiteHomeIndexPage } from './index';
|
||||
import { CoreSiteHomeComponentsModule } from '../../components/components.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CoreSiteHomeIndexPage,
|
||||
],
|
||||
imports: [
|
||||
CoreSiteHomeComponentsModule,
|
||||
IonicPageModule.forChild(CoreSiteHomeIndexPage),
|
||||
TranslateModule.forChild()
|
||||
]
|
||||
})
|
||||
export class CoreSiteHomeIndexPageModule {}
|
|
@ -0,0 +1,37 @@
|
|||
// (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 } from '@angular/core';
|
||||
import { IonicPage, NavParams, NavController } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../../../../providers/sites';
|
||||
import { CoreCourseHelperProvider } from '../../../course/providers/helper';
|
||||
|
||||
/**
|
||||
* Page that displays site home index.
|
||||
*/
|
||||
@IonicPage({segment: 'core-sitehome-index'})
|
||||
@Component({
|
||||
selector: 'page-core-sitehome-index',
|
||||
templateUrl: 'index.html',
|
||||
})
|
||||
export class CoreSiteHomeIndexPage {
|
||||
|
||||
constructor(navParams: NavParams, navCtrl: NavController, courseHelper: CoreCourseHelperProvider,
|
||||
sitesProvider: CoreSitesProvider) {
|
||||
let module = navParams.get('module');
|
||||
if (module) {
|
||||
courseHelper.openModule(navCtrl, module, sitesProvider.getCurrentSite().getSiteHomeId());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
// (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 { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreContentLinksHandlerBase } from '../../contentlinks/classes/base-handler';
|
||||
import { CoreContentLinksAction } from '../../contentlinks/providers/delegate';
|
||||
import { CoreLoginHelperProvider } from '../../login/providers/helper';
|
||||
import { CoreSiteHomeProvider } from './sitehome';
|
||||
|
||||
/**
|
||||
* Handler to treat links to site home index.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreSiteHomeIndexLinkHandler extends CoreContentLinksHandlerBase {
|
||||
name = 'CoreSiteHomeIndexLinkHandler';
|
||||
featureName = '$mmSideMenuDelegate_mmaFrontpage';
|
||||
pattern = /\/course\/view\.php.*([\?\&]id=\d+)/;
|
||||
|
||||
constructor(private sitesProvider: CoreSitesProvider, private siteHomeProvider: CoreSiteHomeProvider,
|
||||
private loginHelper: CoreLoginHelperProvider) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of actions for a link (url).
|
||||
*
|
||||
* @param {string[]} siteIds List of sites the URL belongs to.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||
*/
|
||||
getActions(siteIds: string[], url: string, params: any, courseId?: number) :
|
||||
CoreContentLinksAction[]|Promise<CoreContentLinksAction[]> {
|
||||
return [{
|
||||
action: (siteId, navCtrl?) => {
|
||||
// Always use redirect to make it the new history root (to avoid "loops" in history).
|
||||
this.loginHelper.redirect('CoreSiteHomeIndexPage', undefined, siteId);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the handler is enabled for a certain site (site + user) and a URL.
|
||||
* If not defined, defaults to true.
|
||||
*
|
||||
* @param {string} siteId The site ID.
|
||||
* @param {string} url The URL to treat.
|
||||
* @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
|
||||
* @param {number} [courseId] Course ID related to the URL. Optional but recommended.
|
||||
* @return {boolean|Promise<boolean>} Whether the handler is enabled for the URL and site.
|
||||
*/
|
||||
isEnabled(siteId: string, url: string, params: any, courseId?: number) : boolean|Promise<boolean> {
|
||||
courseId = parseInt(params.id, 10);
|
||||
if (!courseId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
if (courseId != site.getSiteHomeId()) {
|
||||
// The course is not site home.
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.siteHomeProvider.isAvailable(siteId).then(() => {
|
||||
return true;
|
||||
}).catch(() => {
|
||||
return false;
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
// (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 '../../mainmenu/providers/delegate';
|
||||
import { CoreCoursesMyOverviewProvider } from '../../courses/providers/my-overview';
|
||||
|
||||
/**
|
||||
* Handler to add Site Home into main menu.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreSiteHomeMainMenuHandler implements CoreMainMenuHandler {
|
||||
name = 'CoreSiteHome';
|
||||
priority = 1000;
|
||||
isOverviewEnabled: boolean;
|
||||
|
||||
constructor(private siteHomeProvider: CoreSiteHomeProvider, private myOverviewProvider: CoreCoursesMyOverviewProvider) {}
|
||||
|
||||
/**
|
||||
* 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.myOverviewProvider.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'
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
// (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 { CoreLoggerProvider } from '../../../providers/logger';
|
||||
import { CoreSitesProvider } from '../../../providers/sites';
|
||||
import { CoreSite } from '../../../classes/site';
|
||||
import { CoreCourseProvider } from '../../course/providers/course';
|
||||
|
||||
/**
|
||||
* Service that provides some features regarding site home.
|
||||
*/
|
||||
@Injectable()
|
||||
export class CoreSiteHomeProvider {
|
||||
protected logger;
|
||||
|
||||
constructor(logger: CoreLoggerProvider, private sitesProvider: CoreSitesProvider, private courseProvider: CoreCourseProvider) {
|
||||
this.logger = logger.getInstance('CoreSiteHomeProvider');
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the frontpage is available for the current site.
|
||||
*
|
||||
* @param {string} [siteId] The site ID. If not defined, current site.
|
||||
* @return {Promise<boolean>} Promise resolved with boolean: whether it's available.
|
||||
*/
|
||||
isAvailable(siteId?: string) : Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
// First check if it's disabled.
|
||||
if (this.isDisabledInSite(site)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Use a WS call to check if there's content in the site home.
|
||||
const siteHomeId = site.getSiteHomeId(),
|
||||
preSets = {emergencyCache: false};
|
||||
|
||||
this.logger.debug('Using WS call to check if site home is available.');
|
||||
|
||||
return this.courseProvider.getSections(siteHomeId, false, true, preSets, site.id).then((sections) : any => {
|
||||
if (!sections || !sections.length) {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
for (let i = 0; i < sections.length; i++) {
|
||||
let section = sections[i];
|
||||
if (section.summary || (section.modules && section.modules.length)) {
|
||||
// It has content, return true.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return Promise.reject(null);
|
||||
}).catch(() => {
|
||||
const config = site.getStoredConfig();
|
||||
if (config && config.frontpageloggedin) {
|
||||
const items = config.frontpageloggedin.split(',');
|
||||
if (items.length > 0) {
|
||||
// It's enabled.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}).catch(() => {
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Site Home is disabled in a certain site.
|
||||
*
|
||||
* @param {string} [siteId] Site Id. If not defined, use current site.
|
||||
* @return {Promise<boolean>} Promise resolved with true if disabled, rejected or resolved with false otherwise.
|
||||
*/
|
||||
isDisabled(siteId?: string) : Promise<boolean> {
|
||||
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||
return this.isDisabledInSite(site);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if Site Home 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('$mmSideMenuDelegate_mmaFrontpage');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
// (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 { CoreSiteHomeProvider } from './providers/sitehome';
|
||||
import { CoreSiteHomeMainMenuHandler } from './providers/mainmenu-handler';
|
||||
import { CoreSiteHomeIndexLinkHandler } from './providers/index-link-handler';
|
||||
import { CoreMainMenuDelegate } from '../mainmenu/providers/delegate';
|
||||
import { CoreContentLinksDelegate } from '../contentlinks/providers/delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
],
|
||||
providers: [
|
||||
CoreSiteHomeProvider,
|
||||
CoreSiteHomeMainMenuHandler,
|
||||
CoreSiteHomeIndexLinkHandler
|
||||
],
|
||||
exports: []
|
||||
})
|
||||
export class CoreSiteHomeModule {
|
||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, contentLinksDelegate: CoreContentLinksDelegate,
|
||||
mainMenuHandler: CoreSiteHomeMainMenuHandler, indexLinkHandler: CoreSiteHomeIndexLinkHandler) {
|
||||
mainMenuDelegate.registerHandler(mainMenuHandler);
|
||||
contentLinksDelegate.registerHandler(indexLinkHandler);
|
||||
}
|
||||
}
|
|
@ -13,7 +13,7 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Directive, ElementRef, Input, Output, EventEmitter, OnChanges, SimpleChange } from '@angular/core';
|
||||
import { Platform } from 'ionic-angular';
|
||||
import { Platform, NavController } from 'ionic-angular';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { CoreAppProvider } from '../providers/app';
|
||||
import { CoreFilepoolProvider } from '../providers/filepool';
|
||||
|
@ -26,6 +26,7 @@ import { CoreUtilsProvider } from '../providers/utils/utils';
|
|||
import { CoreSite } from '../classes/site';
|
||||
import { CoreLinkDirective } from '../directives/link';
|
||||
import { CoreExternalContentDirective } from '../directives/external-content';
|
||||
import { CoreContentLinksHelperProvider } from '../core/contentlinks/providers/helper';
|
||||
|
||||
/**
|
||||
* Directive to format text rendered. It renders the HTML and treats all links and media, using CoreLinkDirective
|
||||
|
@ -60,7 +61,8 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
constructor(element: ElementRef, private sitesProvider: CoreSitesProvider, private domUtils: CoreDomUtilsProvider,
|
||||
private textUtils: CoreTextUtilsProvider, private translate: TranslateService, private platform: Platform,
|
||||
private utils: CoreUtilsProvider, private urlUtils: CoreUrlUtilsProvider, private loggerProvider: CoreLoggerProvider,
|
||||
private filepoolProvider: CoreFilepoolProvider, private appProvider: CoreAppProvider) {
|
||||
private filepoolProvider: CoreFilepoolProvider, private appProvider: CoreAppProvider,
|
||||
private contentLinksHelper: CoreContentLinksHelperProvider, private navCtrl: NavController) {
|
||||
this.element = element.nativeElement;
|
||||
this.element.classList.add('opacity-hide'); // Hide contents until they're treated.
|
||||
this.afterRender = new EventEmitter();
|
||||
|
@ -274,7 +276,8 @@ export class CoreFormatTextDirective implements OnChanges {
|
|||
// Important: We need to look for links first because in 'img' we add new links without core-link.
|
||||
anchors.forEach((anchor) => {
|
||||
// Angular 2 doesn't let adding directives dynamically. Create the CoreLinkDirective manually.
|
||||
let linkDir = new CoreLinkDirective(anchor, this.domUtils, this.utils, this.sitesProvider, this.urlUtils);
|
||||
let linkDir = new CoreLinkDirective(anchor, this.domUtils, this.utils, this.sitesProvider, this.urlUtils,
|
||||
this.contentLinksHelper, this.navCtrl);
|
||||
linkDir.capture = true;
|
||||
linkDir.ngOnInit();
|
||||
|
||||
|
|
|
@ -13,10 +13,12 @@
|
|||
// limitations under the License.
|
||||
|
||||
import { Directive, Input, OnInit, ElementRef } from '@angular/core';
|
||||
import { NavController } from 'ionic-angular';
|
||||
import { CoreSitesProvider } from '../providers/sites';
|
||||
import { CoreDomUtilsProvider } from '../providers/utils/dom';
|
||||
import { CoreUrlUtilsProvider } from '../providers/utils/url';
|
||||
import { CoreUtilsProvider } from '../providers/utils/utils';
|
||||
import { CoreContentLinksHelperProvider } from '../core/contentlinks/providers/helper';
|
||||
import { CoreConfigConstants } from '../configconstants';
|
||||
|
||||
/**
|
||||
|
@ -36,7 +38,8 @@ export class CoreLinkDirective implements OnInit {
|
|||
protected element: HTMLElement;
|
||||
|
||||
constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider,
|
||||
private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider) {
|
||||
private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider,
|
||||
private contentLinksHelper: CoreContentLinksHelperProvider, private navCtrl: NavController) {
|
||||
// This directive can be added dynamically. In that case, the first param is the anchor HTMLElement.
|
||||
this.element = element.nativeElement || element;
|
||||
}
|
||||
|
@ -56,12 +59,11 @@ export class CoreLinkDirective implements OnInit {
|
|||
event.stopPropagation();
|
||||
|
||||
if (this.utils.isTrueOrOne(this.capture)) {
|
||||
// @todo: Handle link using content links helper.
|
||||
// $mmContentLinksHelper.handleLink(href).then((treated) => {
|
||||
// if (!treated) {
|
||||
this.contentLinksHelper.handleLink(href, undefined, this.navCtrl).then((treated) => {
|
||||
if (!treated) {
|
||||
this.navigate(href);
|
||||
// }
|
||||
// });
|
||||
}
|
||||
});
|
||||
} else {
|
||||
this.navigate(href);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue