commit
aa3e894248
|
@ -420,6 +420,7 @@
|
||||||
"addon.mod_assign_submission_onlinetext.wordlimitexceeded": "assignsubmission_onlinetext",
|
"addon.mod_assign_submission_onlinetext.wordlimitexceeded": "assignsubmission_onlinetext",
|
||||||
"addon.mod_book.errorchapter": "book",
|
"addon.mod_book.errorchapter": "book",
|
||||||
"addon.mod_book.modulenameplural": "book",
|
"addon.mod_book.modulenameplural": "book",
|
||||||
|
"addon.mod_book.tagarea_book_chapters": "book",
|
||||||
"addon.mod_book.toc": "book",
|
"addon.mod_book.toc": "book",
|
||||||
"addon.mod_chat.beep": "chat",
|
"addon.mod_chat.beep": "chat",
|
||||||
"addon.mod_chat.chatreport": "chat",
|
"addon.mod_chat.chatreport": "chat",
|
||||||
|
@ -479,6 +480,7 @@
|
||||||
"addon.mod_data.confirmdeleterecord": "data",
|
"addon.mod_data.confirmdeleterecord": "data",
|
||||||
"addon.mod_data.descending": "data",
|
"addon.mod_data.descending": "data",
|
||||||
"addon.mod_data.disapprove": "data",
|
"addon.mod_data.disapprove": "data",
|
||||||
|
"addon.mod_data.edittagsnotsupported": "local_moodlemobileapp",
|
||||||
"addon.mod_data.emptyaddform": "data",
|
"addon.mod_data.emptyaddform": "data",
|
||||||
"addon.mod_data.entrieslefttoadd": "data",
|
"addon.mod_data.entrieslefttoadd": "data",
|
||||||
"addon.mod_data.entrieslefttoaddtoview": "data",
|
"addon.mod_data.entrieslefttoaddtoview": "data",
|
||||||
|
@ -503,8 +505,10 @@
|
||||||
"addon.mod_data.recorddisapproved": "data",
|
"addon.mod_data.recorddisapproved": "data",
|
||||||
"addon.mod_data.resetsettings": "data",
|
"addon.mod_data.resetsettings": "data",
|
||||||
"addon.mod_data.search": "data",
|
"addon.mod_data.search": "data",
|
||||||
|
"addon.mod_data.searchbytagsnotsupported": "local_moodlemobileapp",
|
||||||
"addon.mod_data.selectedrequired": "data",
|
"addon.mod_data.selectedrequired": "data",
|
||||||
"addon.mod_data.single": "data",
|
"addon.mod_data.single": "data",
|
||||||
|
"addon.mod_data.tagarea_data_records": "data",
|
||||||
"addon.mod_data.timeadded": "data",
|
"addon.mod_data.timeadded": "data",
|
||||||
"addon.mod_data.timemodified": "data",
|
"addon.mod_data.timemodified": "data",
|
||||||
"addon.mod_data.usedate": "data",
|
"addon.mod_data.usedate": "data",
|
||||||
|
@ -597,6 +601,7 @@
|
||||||
"addon.mod_forum.reply": "forum",
|
"addon.mod_forum.reply": "forum",
|
||||||
"addon.mod_forum.replyplaceholder": "forum",
|
"addon.mod_forum.replyplaceholder": "forum",
|
||||||
"addon.mod_forum.subject": "forum",
|
"addon.mod_forum.subject": "forum",
|
||||||
|
"addon.mod_forum.tagarea_forum_posts": "forum",
|
||||||
"addon.mod_forum.thisforumhasduedate": "forum",
|
"addon.mod_forum.thisforumhasduedate": "forum",
|
||||||
"addon.mod_forum.thisforumisdue": "forum",
|
"addon.mod_forum.thisforumisdue": "forum",
|
||||||
"addon.mod_forum.unlockdiscussion": "forum",
|
"addon.mod_forum.unlockdiscussion": "forum",
|
||||||
|
@ -631,6 +636,7 @@
|
||||||
"addon.mod_glossary.modulenameplural": "glossary",
|
"addon.mod_glossary.modulenameplural": "glossary",
|
||||||
"addon.mod_glossary.noentriesfound": "local_moodlemobileapp",
|
"addon.mod_glossary.noentriesfound": "local_moodlemobileapp",
|
||||||
"addon.mod_glossary.searchquery": "local_moodlemobileapp",
|
"addon.mod_glossary.searchquery": "local_moodlemobileapp",
|
||||||
|
"addon.mod_glossary.tagarea_glossary_entries": "glossary",
|
||||||
"addon.mod_imscp.deploymenterror": "imscp",
|
"addon.mod_imscp.deploymenterror": "imscp",
|
||||||
"addon.mod_imscp.modulenameplural": "imscp",
|
"addon.mod_imscp.modulenameplural": "imscp",
|
||||||
"addon.mod_imscp.showmoduledescription": "local_moodlemobileapp",
|
"addon.mod_imscp.showmoduledescription": "local_moodlemobileapp",
|
||||||
|
@ -887,6 +893,7 @@
|
||||||
"addon.mod_wiki.pageexists": "wiki",
|
"addon.mod_wiki.pageexists": "wiki",
|
||||||
"addon.mod_wiki.pagename": "wiki",
|
"addon.mod_wiki.pagename": "wiki",
|
||||||
"addon.mod_wiki.subwiki": "local_moodlemobileapp",
|
"addon.mod_wiki.subwiki": "local_moodlemobileapp",
|
||||||
|
"addon.mod_wiki.tagarea_wiki_pages": "wiki",
|
||||||
"addon.mod_wiki.titleshouldnotbeempty": "local_moodlemobileapp",
|
"addon.mod_wiki.titleshouldnotbeempty": "local_moodlemobileapp",
|
||||||
"addon.mod_wiki.viewpage": "local_moodlemobileapp",
|
"addon.mod_wiki.viewpage": "local_moodlemobileapp",
|
||||||
"addon.mod_wiki.wikipage": "local_moodlemobileapp",
|
"addon.mod_wiki.wikipage": "local_moodlemobileapp",
|
||||||
|
@ -1868,6 +1875,20 @@
|
||||||
"core.submit": "moodle",
|
"core.submit": "moodle",
|
||||||
"core.success": "moodle",
|
"core.success": "moodle",
|
||||||
"core.tablet": "local_moodlemobileapp",
|
"core.tablet": "local_moodlemobileapp",
|
||||||
|
"core.tag.defautltagcoll": "moodle",
|
||||||
|
"core.tag.errorareanotsupported": "local_moodlemobileapp",
|
||||||
|
"core.tag.inalltagcoll": "moodle",
|
||||||
|
"core.tag.itemstaggedwith": "moodle",
|
||||||
|
"core.tag.notagsfound": "moodle",
|
||||||
|
"core.tag.searchtags": "moodle",
|
||||||
|
"core.tag.showingfirsttags": "moodle",
|
||||||
|
"core.tag.tag": "moodle",
|
||||||
|
"core.tag.tagarea_course": "moodle",
|
||||||
|
"core.tag.tagarea_course_modules": "moodle",
|
||||||
|
"core.tag.tagarea_post": "moodle",
|
||||||
|
"core.tag.tagarea_user": "moodle",
|
||||||
|
"core.tag.tags": "moodle",
|
||||||
|
"core.tag.warningareasnotsupported": "local_moodlemobileapp",
|
||||||
"core.teachers": "moodle",
|
"core.teachers": "moodle",
|
||||||
"core.thereisdatatosync": "local_moodlemobileapp",
|
"core.thereisdatatosync": "local_moodlemobileapp",
|
||||||
"core.thisdirection": "langconfig",
|
"core.thisdirection": "langconfig",
|
||||||
|
|
|
@ -17,12 +17,14 @@ import { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate';
|
||||||
import { CoreUserDelegate } from '@core/user/providers/user-delegate';
|
import { CoreUserDelegate } from '@core/user/providers/user-delegate';
|
||||||
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
import { CoreCourseOptionsDelegate } from '@core/course/providers/options-delegate';
|
||||||
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
import { AddonBlogProvider } from './providers/blog';
|
import { AddonBlogProvider } from './providers/blog';
|
||||||
import { AddonBlogMainMenuHandler } from './providers/mainmenu-handler';
|
import { AddonBlogMainMenuHandler } from './providers/mainmenu-handler';
|
||||||
import { AddonBlogUserHandler } from './providers/user-handler';
|
import { AddonBlogUserHandler } from './providers/user-handler';
|
||||||
import { AddonBlogCourseOptionHandler } from './providers/course-option-handler';
|
import { AddonBlogCourseOptionHandler } from './providers/course-option-handler';
|
||||||
import { AddonBlogComponentsModule } from './components/components.module';
|
import { AddonBlogComponentsModule } from './components/components.module';
|
||||||
import { AddonBlogIndexLinkHandler } from './providers/index-link-handler';
|
import { AddonBlogIndexLinkHandler } from './providers/index-link-handler';
|
||||||
|
import { AddonBlogTagAreaHandler } from './providers/tag-area-handler';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -35,17 +37,20 @@ import { AddonBlogIndexLinkHandler } from './providers/index-link-handler';
|
||||||
AddonBlogMainMenuHandler,
|
AddonBlogMainMenuHandler,
|
||||||
AddonBlogUserHandler,
|
AddonBlogUserHandler,
|
||||||
AddonBlogCourseOptionHandler,
|
AddonBlogCourseOptionHandler,
|
||||||
AddonBlogIndexLinkHandler
|
AddonBlogIndexLinkHandler,
|
||||||
|
AddonBlogTagAreaHandler
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AddonBlogModule {
|
export class AddonBlogModule {
|
||||||
constructor(mainMenuDelegate: CoreMainMenuDelegate, menuHandler: AddonBlogMainMenuHandler,
|
constructor(mainMenuDelegate: CoreMainMenuDelegate, menuHandler: AddonBlogMainMenuHandler,
|
||||||
userHandler: AddonBlogUserHandler, userDelegate: CoreUserDelegate,
|
userHandler: AddonBlogUserHandler, userDelegate: CoreUserDelegate,
|
||||||
courseOptionHandler: AddonBlogCourseOptionHandler, courseOptionsDelegate: CoreCourseOptionsDelegate,
|
courseOptionHandler: AddonBlogCourseOptionHandler, courseOptionsDelegate: CoreCourseOptionsDelegate,
|
||||||
linkHandler: AddonBlogIndexLinkHandler, contentLinksDelegate: CoreContentLinksDelegate) {
|
linkHandler: AddonBlogIndexLinkHandler, contentLinksDelegate: CoreContentLinksDelegate,
|
||||||
|
tagAreaDelegate: CoreTagAreaDelegate, tagAreaHandler: AddonBlogTagAreaHandler) {
|
||||||
mainMenuDelegate.registerHandler(menuHandler);
|
mainMenuDelegate.registerHandler(menuHandler);
|
||||||
userDelegate.registerHandler(userHandler);
|
userDelegate.registerHandler(userHandler);
|
||||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||||
contentLinksDelegate.registerHandler(linkHandler);
|
contentLinksDelegate.registerHandler(linkHandler);
|
||||||
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
import { CoreCommentsComponentsModule } from '@core/comments/components/components.module';
|
import { CoreCommentsComponentsModule } from '@core/comments/components/components.module';
|
||||||
|
import { CoreTagComponentsModule } from '@core/tag/components/components.module';
|
||||||
import { AddonBlogEntriesComponent } from './entries/entries';
|
import { AddonBlogEntriesComponent } from './entries/entries';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -33,7 +34,8 @@ import { AddonBlogEntriesComponent } from './entries/entries';
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
CorePipesModule,
|
CorePipesModule,
|
||||||
CoreCommentsComponentsModule
|
CoreCommentsComponentsModule,
|
||||||
|
CoreTagComponentsModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
],
|
],
|
||||||
|
|
|
@ -29,6 +29,10 @@
|
||||||
</ion-item>
|
</ion-item>
|
||||||
<ion-card-content>
|
<ion-card-content>
|
||||||
<core-format-text [text]="entry.summary" [component]="this.component" [componentId]="entry.id"></core-format-text>
|
<core-format-text [text]="entry.summary" [component]="this.component" [componentId]="entry.id"></core-format-text>
|
||||||
|
<ion-item text-wrap *ngIf="tagsEnabled && entry.tags && entry.tags.length > 0">
|
||||||
|
<div item-start>{{ 'core.tag.tags' | translate }}:</div>
|
||||||
|
<core-tag-list [tags]="entry.tags"></core-tag-list>
|
||||||
|
</ion-item>
|
||||||
<ion-item *ngIf="commentsEnabled">
|
<ion-item *ngIf="commentsEnabled">
|
||||||
<core-comments [component]="this.component" [itemId]="entry.id" area="format_blog" [instanceId]="entry.userid" contextLevel="user"></core-comments>
|
<core-comments [component]="this.component" [itemId]="entry.id" area="format_blog" [instanceId]="entry.userid" contextLevel="user"></core-comments>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
import { AddonBlogProvider } from '../../providers/blog';
|
import { AddonBlogProvider } from '../../providers/blog';
|
||||||
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
import { CoreCommentsProvider } from '@core/comments/providers/comments';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays the blog entries.
|
* Component that displays the blog entries.
|
||||||
|
@ -49,10 +50,11 @@ export class AddonBlogEntriesComponent implements OnInit {
|
||||||
onlyMyEntries = false;
|
onlyMyEntries = false;
|
||||||
component = AddonBlogProvider.COMPONENT;
|
component = AddonBlogProvider.COMPONENT;
|
||||||
commentsEnabled: boolean;
|
commentsEnabled: boolean;
|
||||||
|
tagsEnabled: boolean;
|
||||||
|
|
||||||
constructor(protected blogProvider: AddonBlogProvider, protected domUtils: CoreDomUtilsProvider,
|
constructor(protected blogProvider: AddonBlogProvider, protected domUtils: CoreDomUtilsProvider,
|
||||||
protected userProvider: CoreUserProvider, sitesProvider: CoreSitesProvider,
|
protected userProvider: CoreUserProvider, sitesProvider: CoreSitesProvider,
|
||||||
protected commentsProvider: CoreCommentsProvider) {
|
protected commentsProvider: CoreCommentsProvider, private tagProvider: CoreTagProvider) {
|
||||||
this.currentUserId = sitesProvider.getCurrentSiteUserId();
|
this.currentUserId = sitesProvider.getCurrentSiteUserId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,6 +87,7 @@ export class AddonBlogEntriesComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
this.commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite();
|
this.commentsEnabled = !this.commentsProvider.areCommentsDisabledInSite();
|
||||||
|
this.tagsEnabled = this.tagProvider.areTagsAvailableInSite();
|
||||||
|
|
||||||
this.fetchEntries().then(() => {
|
this.fetchEntries().then(() => {
|
||||||
this.blogProvider.logView(this.filter).catch(() => {
|
this.blogProvider.logView(this.filter).catch(() => {
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreTagHelperProvider } from '@core/tag/providers/helper';
|
||||||
|
import { CoreTagFeedComponent } from '@core/tag/components/feed/feed';
|
||||||
|
import { AddonBlogProvider } from './blog';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonBlogTagAreaHandler implements CoreTagAreaHandler {
|
||||||
|
name = 'AddonBlogTagAreaHandler';
|
||||||
|
type = 'core/post';
|
||||||
|
|
||||||
|
constructor(private tagHelper: CoreTagHelperProvider, private blogProvider: AddonBlogProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return this.blogProvider.isPluginEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]> {
|
||||||
|
return this.tagHelper.parseFeedContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any> {
|
||||||
|
return CoreTagFeedComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,6 +22,8 @@ import { AddonModBookPrefetchHandler } from './providers/prefetch-handler';
|
||||||
import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
|
import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
|
||||||
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||||
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
|
import { AddonModBookTagAreaHandler } from './providers/tag-area-handler';
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const ADDON_MOD_BOOK_PROVIDERS: any[] = [
|
export const ADDON_MOD_BOOK_PROVIDERS: any[] = [
|
||||||
|
@ -39,18 +41,21 @@ export const ADDON_MOD_BOOK_PROVIDERS: any[] = [
|
||||||
AddonModBookModuleHandler,
|
AddonModBookModuleHandler,
|
||||||
AddonModBookLinkHandler,
|
AddonModBookLinkHandler,
|
||||||
AddonModBookListLinkHandler,
|
AddonModBookListLinkHandler,
|
||||||
AddonModBookPrefetchHandler
|
AddonModBookPrefetchHandler,
|
||||||
|
AddonModBookTagAreaHandler
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AddonModBookModule {
|
export class AddonModBookModule {
|
||||||
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModBookModuleHandler,
|
constructor(moduleDelegate: CoreCourseModuleDelegate, moduleHandler: AddonModBookModuleHandler,
|
||||||
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModBookLinkHandler,
|
contentLinksDelegate: CoreContentLinksDelegate, linkHandler: AddonModBookLinkHandler,
|
||||||
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModBookPrefetchHandler,
|
prefetchDelegate: CoreCourseModulePrefetchDelegate, prefetchHandler: AddonModBookPrefetchHandler,
|
||||||
listLinkHandler: AddonModBookListLinkHandler) {
|
listLinkHandler: AddonModBookListLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
||||||
|
tagAreaHandler: AddonModBookTagAreaHandler) {
|
||||||
|
|
||||||
moduleDelegate.registerHandler(moduleHandler);
|
moduleDelegate.registerHandler(moduleHandler);
|
||||||
contentLinksDelegate.registerHandler(linkHandler);
|
contentLinksDelegate.registerHandler(linkHandler);
|
||||||
contentLinksDelegate.registerHandler(listLinkHandler);
|
contentLinksDelegate.registerHandler(listLinkHandler);
|
||||||
prefetchDelegate.registerHandler(prefetchHandler);
|
prefetchDelegate.registerHandler(prefetchHandler);
|
||||||
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
|
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
|
||||||
import { AddonModBookIndexComponent } from './index/index';
|
import { AddonModBookIndexComponent } from './index/index';
|
||||||
|
import { CoreTagComponentsModule } from '@core/tag/components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -31,7 +32,8 @@ import { AddonModBookIndexComponent } from './index/index';
|
||||||
TranslateModule.forChild(),
|
TranslateModule.forChild(),
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
CoreCourseComponentsModule
|
CoreCourseComponentsModule,
|
||||||
|
CoreTagComponentsModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
],
|
],
|
||||||
|
|
|
@ -21,6 +21,10 @@
|
||||||
<div padding>
|
<div padding>
|
||||||
<core-navigation-bar [previous]="previousChapter > 0 && previousChapter" [next]="nextChapter > 0 && nextChapter" (action)="changeChapter($event)"></core-navigation-bar>
|
<core-navigation-bar [previous]="previousChapter > 0 && previousChapter" [next]="nextChapter > 0 && nextChapter" (action)="changeChapter($event)"></core-navigation-bar>
|
||||||
<core-format-text [component]="component" [componentId]="componentId" [text]="chapterContent"></core-format-text>
|
<core-format-text [component]="component" [componentId]="componentId" [text]="chapterContent"></core-format-text>
|
||||||
|
<div margin-top *ngIf="tagsEnabled && contentsMap && contentsMap[currentChapter] && contentsMap[currentChapter].tags && contentsMap[currentChapter].tags.length > 0">
|
||||||
|
<b>{{ 'core.tag.tags' | translate }}:</b>
|
||||||
|
<core-tag-list [tags]="contentsMap[currentChapter].tags"></core-tag-list>
|
||||||
|
</div>
|
||||||
<core-navigation-bar [previous]="previousChapter > 0 && previousChapter" [next]="nextChapter > 0 && nextChapter" (action)="changeChapter($event)"></core-navigation-bar>
|
<core-navigation-bar [previous]="previousChapter > 0 && previousChapter" [next]="nextChapter > 0 && nextChapter" (action)="changeChapter($event)"></core-navigation-bar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseModuleMainResourceComponent } from '@core/course/classes/main-resource-component';
|
import { CoreCourseModuleMainResourceComponent } from '@core/course/classes/main-resource-component';
|
||||||
import { AddonModBookProvider, AddonModBookContentsMap, AddonModBookTocChapter } from '../../providers/book';
|
import { AddonModBookProvider, AddonModBookContentsMap, AddonModBookTocChapter } from '../../providers/book';
|
||||||
import { AddonModBookPrefetchHandler } from '../../providers/prefetch-handler';
|
import { AddonModBookPrefetchHandler } from '../../providers/prefetch-handler';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays a book.
|
* Component that displays a book.
|
||||||
|
@ -34,6 +35,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
chapterContent: string;
|
chapterContent: string;
|
||||||
previousChapter: string;
|
previousChapter: string;
|
||||||
nextChapter: string;
|
nextChapter: string;
|
||||||
|
tagsEnabled: boolean;
|
||||||
|
|
||||||
protected chapters: AddonModBookTocChapter[];
|
protected chapters: AddonModBookTocChapter[];
|
||||||
protected currentChapter: string;
|
protected currentChapter: string;
|
||||||
|
@ -41,7 +43,7 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
|
|
||||||
constructor(injector: Injector, private bookProvider: AddonModBookProvider, private courseProvider: CoreCourseProvider,
|
constructor(injector: Injector, private bookProvider: AddonModBookProvider, private courseProvider: CoreCourseProvider,
|
||||||
private appProvider: CoreAppProvider, private prefetchDelegate: AddonModBookPrefetchHandler,
|
private appProvider: CoreAppProvider, private prefetchDelegate: AddonModBookPrefetchHandler,
|
||||||
private modalCtrl: ModalController, @Optional() private content: Content) {
|
private modalCtrl: ModalController, private tagProvider: CoreTagProvider, @Optional() private content: Content) {
|
||||||
super(injector);
|
super(injector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +53,8 @@ export class AddonModBookIndexComponent extends CoreCourseModuleMainResourceComp
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
|
|
||||||
|
this.tagsEnabled = this.tagProvider.areTagsAvailableInSite();
|
||||||
|
|
||||||
this.loadContent();
|
this.loadContent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{
|
{
|
||||||
"errorchapter": "Error reading chapter of book.",
|
"errorchapter": "Error reading chapter of book.",
|
||||||
"modulenameplural": "Books",
|
"modulenameplural": "Books",
|
||||||
|
"tagarea_book_chapters": "Book chapters",
|
||||||
"toc": "Table of contents"
|
"toc": "Table of contents"
|
||||||
}
|
}
|
|
@ -24,6 +24,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreCourseProvider } from '@core/course/providers/course';
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
import { CoreCourseLogHelperProvider } from '@core/course/providers/log-helper';
|
||||||
import { CoreSite } from '@classes/site';
|
import { CoreSite } from '@classes/site';
|
||||||
|
import { CoreTagItem } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A book chapter inside the toc list.
|
* A book chapter inside the toc list.
|
||||||
|
@ -52,7 +53,13 @@ export interface AddonModBookTocChapter {
|
||||||
* Map of book contents. For each chapter it has its index URL and the list of paths of the files the chapter has. Each path
|
* Map of book contents. For each chapter it has its index URL and the list of paths of the files the chapter has. Each path
|
||||||
* is identified by the relative path in the book, and the value is the URL of the file.
|
* is identified by the relative path in the book, and the value is the URL of the file.
|
||||||
*/
|
*/
|
||||||
export type AddonModBookContentsMap = {[chapter: string]: {indexUrl?: string, paths: {[path: string]: string}}};
|
export type AddonModBookContentsMap = {
|
||||||
|
[chapter: string]: {
|
||||||
|
indexUrl?: string,
|
||||||
|
paths: {[path: string]: string},
|
||||||
|
tags?: CoreTagItem[]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Service that provides some features for books.
|
* Service that provides some features for books.
|
||||||
|
@ -203,8 +210,9 @@ export class AddonModBookProvider {
|
||||||
map[chapter] = map[chapter] || { paths: {} };
|
map[chapter] = map[chapter] || { paths: {} };
|
||||||
|
|
||||||
if (content.filename == 'index.html' && filepathIsChapter) {
|
if (content.filename == 'index.html' && filepathIsChapter) {
|
||||||
// Index of the chapter, set indexUrl of the chapter.
|
// Index of the chapter, set indexUrl and tags of the chapter.
|
||||||
map[chapter].indexUrl = content.fileurl;
|
map[chapter].indexUrl = content.fileurl;
|
||||||
|
map[chapter].tags = content.tags;
|
||||||
} else {
|
} else {
|
||||||
if (filepathIsChapter) {
|
if (filepathIsChapter) {
|
||||||
// It's a file in the root folder OR the WS isn't returning the filepath as it should (MDL-53671).
|
// It's a file in the root folder OR the WS isn't returning the filepath as it should (MDL-53671).
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreTagHelperProvider } from '@core/tag/providers/helper';
|
||||||
|
import { CoreTagFeedComponent } from '@core/tag/components/feed/feed';
|
||||||
|
import { CoreCourseProvider } from '@core/course/providers/course';
|
||||||
|
import { CoreUrlUtilsProvider } from '@providers/utils/url';
|
||||||
|
import { AddonModBookProvider } from './book';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonModBookTagAreaHandler implements CoreTagAreaHandler {
|
||||||
|
name = 'AddonModBookTagAreaHandler';
|
||||||
|
type = 'mod_book/book_chapters';
|
||||||
|
|
||||||
|
constructor(private tagHelper: CoreTagHelperProvider, private bookProvider: AddonModBookProvider,
|
||||||
|
private courseProvider: CoreCourseProvider, private urlUtils: CoreUrlUtilsProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return this.bookProvider.isPluginEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]> {
|
||||||
|
const items = this.tagHelper.parseFeedContent(content);
|
||||||
|
|
||||||
|
// Find module ids of the returned books, they are needed by the link delegate.
|
||||||
|
return Promise.all(items.map((item) => {
|
||||||
|
const params = this.urlUtils.extractUrlParams(item.url);
|
||||||
|
if (params.b && !params.id) {
|
||||||
|
const bookId = parseInt(params.b, 10);
|
||||||
|
|
||||||
|
return this.courseProvider.getModuleBasicInfoByInstance(bookId, 'book').then((module) => {
|
||||||
|
item.url += '&id=' + module.id;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})).then(() => {
|
||||||
|
return items;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any> {
|
||||||
|
return CoreTagFeedComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ import { AddonModDataOfflineProvider } from '../../providers/offline';
|
||||||
import { CoreSitesProvider } from '@providers/sites';
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||||
import { CoreUserProvider } from '@core/user/providers/user';
|
import { CoreUserProvider } from '@core/user/providers/user';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays a database action.
|
* Component that displays a database action.
|
||||||
|
@ -41,13 +42,16 @@ export class AddonModDataActionComponent implements OnInit {
|
||||||
rootUrl: string;
|
rootUrl: string;
|
||||||
url: string;
|
url: string;
|
||||||
userPicture: string;
|
userPicture: string;
|
||||||
|
tagsEnabled: boolean;
|
||||||
|
|
||||||
constructor(protected injector: Injector, protected dataProvider: AddonModDataProvider,
|
constructor(protected injector: Injector, protected dataProvider: AddonModDataProvider,
|
||||||
protected dataOffline: AddonModDataOfflineProvider, protected eventsProvider: CoreEventsProvider,
|
protected dataOffline: AddonModDataOfflineProvider, protected eventsProvider: CoreEventsProvider,
|
||||||
sitesProvider: CoreSitesProvider, protected userProvider: CoreUserProvider, private navCtrl: NavController,
|
sitesProvider: CoreSitesProvider, protected userProvider: CoreUserProvider, private navCtrl: NavController,
|
||||||
protected linkHelper: CoreContentLinksHelperProvider, private dataHelper: AddonModDataHelperProvider) {
|
protected linkHelper: CoreContentLinksHelperProvider, private dataHelper: AddonModDataHelperProvider,
|
||||||
|
private tagProvider: CoreTagProvider) {
|
||||||
this.rootUrl = sitesProvider.getCurrentSite().getURL();
|
this.rootUrl = sitesProvider.getCurrentSite().getURL();
|
||||||
this.siteId = sitesProvider.getCurrentSiteId();
|
this.siteId = sitesProvider.getCurrentSiteId();
|
||||||
|
this.tagsEnabled = this.tagProvider.areTagsAvailableInSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -32,3 +32,5 @@
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a *ngIf="action == 'user' && entry" core-user-link [courseId]="database.courseid" [userId]="entry.userid" [title]="entry.fullname">{{entry.fullname}}</a>
|
<a *ngIf="action == 'user' && entry" core-user-link [courseId]="database.courseid" [userId]="entry.userid" [title]="entry.fullname">{{entry.fullname}}</a>
|
||||||
|
|
||||||
|
<core-tag-list *ngIf="tagsEnabled && action == 'tags' && entry" [tags]="entry.tags"></core-tag-list>
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { AddonModDataFieldPluginComponent } from './field-plugin/field-plugin';
|
||||||
import { AddonModDataActionComponent } from './action/action';
|
import { AddonModDataActionComponent } from './action/action';
|
||||||
import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module';
|
import { CoreCompileHtmlComponentModule } from '@core/compile/components/compile-html/compile-html.module';
|
||||||
import { CoreCommentsComponentsModule } from '@core/comments/components/components.module';
|
import { CoreCommentsComponentsModule } from '@core/comments/components/components.module';
|
||||||
|
import { CoreTagComponentsModule } from '@core/tag/components/components.module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
|
@ -41,7 +42,8 @@ import { CoreCommentsComponentsModule } from '@core/comments/components/componen
|
||||||
CorePipesModule,
|
CorePipesModule,
|
||||||
CoreCourseComponentsModule,
|
CoreCourseComponentsModule,
|
||||||
CoreCompileHtmlComponentModule,
|
CoreCompileHtmlComponentModule,
|
||||||
CoreCommentsComponentsModule
|
CoreCommentsComponentsModule,
|
||||||
|
CoreTagComponentsModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
],
|
],
|
||||||
|
|
|
@ -33,6 +33,8 @@ import { AddonModDataSyncCronHandler } from './providers/sync-cron-handler';
|
||||||
import { AddonModDataOfflineProvider } from './providers/offline';
|
import { AddonModDataOfflineProvider } from './providers/offline';
|
||||||
import { AddonModDataFieldsDelegate } from './providers/fields-delegate';
|
import { AddonModDataFieldsDelegate } from './providers/fields-delegate';
|
||||||
import { AddonModDataDefaultFieldHandler } from './providers/default-field-handler';
|
import { AddonModDataDefaultFieldHandler } from './providers/default-field-handler';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
|
import { AddonModDataTagAreaHandler } from './providers/tag-area-handler';
|
||||||
import { AddonModDataFieldModule } from './fields/field.module';
|
import { AddonModDataFieldModule } from './fields/field.module';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
||||||
|
|
||||||
|
@ -67,7 +69,8 @@ export const ADDON_MOD_DATA_PROVIDERS: any[] = [
|
||||||
AddonModDataEditLinkHandler,
|
AddonModDataEditLinkHandler,
|
||||||
AddonModDataListLinkHandler,
|
AddonModDataListLinkHandler,
|
||||||
AddonModDataSyncCronHandler,
|
AddonModDataSyncCronHandler,
|
||||||
AddonModDataDefaultFieldHandler
|
AddonModDataDefaultFieldHandler,
|
||||||
|
AddonModDataTagAreaHandler
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AddonModDataModule {
|
export class AddonModDataModule {
|
||||||
|
@ -77,7 +80,8 @@ export class AddonModDataModule {
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonModDataSyncCronHandler, updateManager: CoreUpdateManagerProvider,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonModDataSyncCronHandler, updateManager: CoreUpdateManagerProvider,
|
||||||
approveLinkHandler: AddonModDataApproveLinkHandler, deleteLinkHandler: AddonModDataDeleteLinkHandler,
|
approveLinkHandler: AddonModDataApproveLinkHandler, deleteLinkHandler: AddonModDataDeleteLinkHandler,
|
||||||
showLinkHandler: AddonModDataShowLinkHandler, editLinkHandler: AddonModDataEditLinkHandler,
|
showLinkHandler: AddonModDataShowLinkHandler, editLinkHandler: AddonModDataEditLinkHandler,
|
||||||
listLinkHandler: AddonModDataListLinkHandler) {
|
listLinkHandler: AddonModDataListLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
||||||
|
tagAreaHandler: AddonModDataTagAreaHandler) {
|
||||||
|
|
||||||
moduleDelegate.registerHandler(moduleHandler);
|
moduleDelegate.registerHandler(moduleHandler);
|
||||||
prefetchDelegate.registerHandler(prefetchHandler);
|
prefetchDelegate.registerHandler(prefetchHandler);
|
||||||
|
@ -88,6 +92,7 @@ export class AddonModDataModule {
|
||||||
contentLinksDelegate.registerHandler(editLinkHandler);
|
contentLinksDelegate.registerHandler(editLinkHandler);
|
||||||
contentLinksDelegate.registerHandler(listLinkHandler);
|
contentLinksDelegate.registerHandler(listLinkHandler);
|
||||||
cronDelegate.register(syncHandler);
|
cronDelegate.register(syncHandler);
|
||||||
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
// Allow migrating the tables from the old app to the new schema.
|
||||||
updateManager.registerSiteTableMigration({
|
updateManager.registerSiteTableMigration({
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
"confirmdeleterecord": "Are you sure you want to delete this entry?",
|
"confirmdeleterecord": "Are you sure you want to delete this entry?",
|
||||||
"descending": "Descending",
|
"descending": "Descending",
|
||||||
"disapprove": "Undo approval",
|
"disapprove": "Undo approval",
|
||||||
|
"edittagsnotsupported": "Sorry, editing tags is not supported by the app.",
|
||||||
"emptyaddform": "You did not fill out any fields!",
|
"emptyaddform": "You did not fill out any fields!",
|
||||||
"entrieslefttoadd": "You must add {{$a.entriesleft}} more entry/entries in order to complete this activity",
|
"entrieslefttoadd": "You must add {{$a.entriesleft}} more entry/entries in order to complete this activity",
|
||||||
"entrieslefttoaddtoview": "You must add {{$a.entrieslefttoview}} more entry/entries before you can view other participants' entries.",
|
"entrieslefttoaddtoview": "You must add {{$a.entrieslefttoview}} more entry/entries before you can view other participants' entries.",
|
||||||
|
@ -34,8 +35,10 @@
|
||||||
"recorddisapproved": "Entry unapproved",
|
"recorddisapproved": "Entry unapproved",
|
||||||
"resetsettings": "Reset filters",
|
"resetsettings": "Reset filters",
|
||||||
"search": "Search",
|
"search": "Search",
|
||||||
|
"searchbytagsnotsupported": "Sorry, searching by tags is not supported by the app.",
|
||||||
"selectedrequired": "All selected required",
|
"selectedrequired": "All selected required",
|
||||||
"single": "View single",
|
"single": "View single",
|
||||||
|
"tagarea_data_records": "Data records",
|
||||||
"timeadded": "Time added",
|
"timeadded": "Time added",
|
||||||
"timemodified": "Time modified",
|
"timemodified": "Time modified",
|
||||||
"usedate": "Include in search."
|
"usedate": "Include in search."
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { AddonModDataHelperProvider } from '../../providers/helper';
|
||||||
import { AddonModDataOfflineProvider } from '../../providers/offline';
|
import { AddonModDataOfflineProvider } from '../../providers/offline';
|
||||||
import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate';
|
import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate';
|
||||||
import { AddonModDataComponentsModule } from '../../components/components.module';
|
import { AddonModDataComponentsModule } from '../../components/components.module';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the view edit page.
|
* Page that displays the view edit page.
|
||||||
|
@ -68,7 +69,8 @@ export class AddonModDataEditPage {
|
||||||
protected courseProvider: CoreCourseProvider, protected dataProvider: AddonModDataProvider,
|
protected courseProvider: CoreCourseProvider, protected dataProvider: AddonModDataProvider,
|
||||||
protected dataOffline: AddonModDataOfflineProvider, protected dataHelper: AddonModDataHelperProvider,
|
protected dataOffline: AddonModDataOfflineProvider, protected dataHelper: AddonModDataHelperProvider,
|
||||||
sitesProvider: CoreSitesProvider, protected navCtrl: NavController, protected translate: TranslateService,
|
sitesProvider: CoreSitesProvider, protected navCtrl: NavController, protected translate: TranslateService,
|
||||||
protected eventsProvider: CoreEventsProvider, protected fileUploaderProvider: CoreFileUploaderProvider) {
|
protected eventsProvider: CoreEventsProvider, protected fileUploaderProvider: CoreFileUploaderProvider,
|
||||||
|
private tagProvider: CoreTagProvider) {
|
||||||
this.module = params.get('module') || {};
|
this.module = params.get('module') || {};
|
||||||
this.entryId = params.get('entryId') || null;
|
this.entryId = params.get('entryId') || null;
|
||||||
this.courseId = params.get('courseId');
|
this.courseId = params.get('courseId');
|
||||||
|
@ -302,6 +304,11 @@ export class AddonModDataEditPage {
|
||||||
template = template.replace(replace, 'field_' + field.id);
|
template = template.replace(replace, 'field_' + field.id);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Editing tags is not supported.
|
||||||
|
replace = new RegExp('##tags##', 'gi');
|
||||||
|
const message = '<p class="item-dimmed">{{ \'addon.mod_data.edittagsnotsupported\' | translate }}</p>';
|
||||||
|
template = template.replace(replace, this.tagProvider.areTagsAvailableInSite() ? message : '');
|
||||||
|
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
import { AddonModDataComponentsModule } from '../../components/components.module';
|
import { AddonModDataComponentsModule } from '../../components/components.module';
|
||||||
import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate';
|
import { AddonModDataFieldsDelegate } from '../../providers/fields-delegate';
|
||||||
import { AddonModDataHelperProvider } from '../../providers/helper';
|
import { AddonModDataHelperProvider } from '../../providers/helper';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Page that displays the search modal.
|
* Page that displays the search modal.
|
||||||
|
@ -42,7 +43,8 @@ export class AddonModDataSearchPage {
|
||||||
|
|
||||||
constructor(params: NavParams, private viewCtrl: ViewController, fb: FormBuilder, protected utils: CoreUtilsProvider,
|
constructor(params: NavParams, private viewCtrl: ViewController, fb: FormBuilder, protected utils: CoreUtilsProvider,
|
||||||
protected domUtils: CoreDomUtilsProvider, protected fieldsDelegate: AddonModDataFieldsDelegate,
|
protected domUtils: CoreDomUtilsProvider, protected fieldsDelegate: AddonModDataFieldsDelegate,
|
||||||
protected textUtils: CoreTextUtilsProvider, protected dataHelper: AddonModDataHelperProvider) {
|
protected textUtils: CoreTextUtilsProvider, protected dataHelper: AddonModDataHelperProvider,
|
||||||
|
private tagProvider: CoreTagProvider) {
|
||||||
this.search = params.get('search');
|
this.search = params.get('search');
|
||||||
this.fields = params.get('fields');
|
this.fields = params.get('fields');
|
||||||
this.data = params.get('data');
|
this.data = params.get('data');
|
||||||
|
@ -117,9 +119,10 @@ export class AddonModDataSearchPage {
|
||||||
[placeholder]="\'addon.mod_data.authorlastname\' | translate" formControlName="lastname"></ion-input></span>';
|
[placeholder]="\'addon.mod_data.authorlastname\' | translate" formControlName="lastname"></ion-input></span>';
|
||||||
template = template.replace(replace, render);
|
template = template.replace(replace, render);
|
||||||
|
|
||||||
// Tags are unsupported right now.
|
// Searching by tags is not supported.
|
||||||
replace = new RegExp('##tags##', 'gi');
|
replace = new RegExp('##tags##', 'gi');
|
||||||
template = template.replace(replace, '');
|
const message = '<p class="item-dimmed">{{ \'addon.mod_data.searchbytagsnotsupported\' | translate }}</p>';
|
||||||
|
template = template.replace(replace, this.tagProvider.areTagsAvailableInSite() ? message : '');
|
||||||
|
|
||||||
return template;
|
return template;
|
||||||
}
|
}
|
||||||
|
|
|
@ -367,6 +367,7 @@ export class AddonModDataHelperProvider {
|
||||||
userpicture: true,
|
userpicture: true,
|
||||||
timeadded: true,
|
timeadded: true,
|
||||||
timemodified: true,
|
timemodified: true,
|
||||||
|
tags: true,
|
||||||
|
|
||||||
edit: record.canmanageentry && !record.deleted, // This already checks capabilities and readonly period.
|
edit: record.canmanageentry && !record.deleted, // This already checks capabilities and readonly period.
|
||||||
delete: record.canmanageentry,
|
delete: record.canmanageentry,
|
||||||
|
@ -377,7 +378,6 @@ export class AddonModDataHelperProvider {
|
||||||
comments: database.comments,
|
comments: database.comments,
|
||||||
|
|
||||||
// Unsupported actions.
|
// Unsupported actions.
|
||||||
tags: false,
|
|
||||||
delcheck: false,
|
delcheck: false,
|
||||||
export: false
|
export: false
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreTagHelperProvider } from '@core/tag/providers/helper';
|
||||||
|
import { CoreTagFeedComponent } from '@core/tag/components/feed/feed';
|
||||||
|
import { AddonModDataProvider } from './data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonModDataTagAreaHandler implements CoreTagAreaHandler {
|
||||||
|
name = 'AddonModDataTagAreaHandler';
|
||||||
|
type = 'mod_data/data_records';
|
||||||
|
|
||||||
|
constructor(private tagHelper: CoreTagHelperProvider, private dataProvider: AddonModDataProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return this.dataProvider.isPluginEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]> {
|
||||||
|
return this.tagHelper.parseFeedContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any> {
|
||||||
|
return CoreTagFeedComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
|
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
|
||||||
import { CoreRatingComponentsModule } from '@core/rating/components/components.module';
|
import { CoreRatingComponentsModule } from '@core/rating/components/components.module';
|
||||||
|
import { CoreTagComponentsModule } from '@core/tag/components/components.module';
|
||||||
import { AddonModForumIndexComponent } from './index/index';
|
import { AddonModForumIndexComponent } from './index/index';
|
||||||
import { AddonModForumPostComponent } from './post/post';
|
import { AddonModForumPostComponent } from './post/post';
|
||||||
|
|
||||||
|
@ -37,7 +38,8 @@ import { AddonModForumPostComponent } from './post/post';
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
CorePipesModule,
|
CorePipesModule,
|
||||||
CoreCourseComponentsModule,
|
CoreCourseComponentsModule,
|
||||||
CoreRatingComponentsModule
|
CoreRatingComponentsModule,
|
||||||
|
CoreTagComponentsModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
],
|
],
|
||||||
|
|
|
@ -30,6 +30,10 @@
|
||||||
</ng-container>
|
</ng-container>
|
||||||
</div>
|
</div>
|
||||||
</ion-card-content>
|
</ion-card-content>
|
||||||
|
<ion-item text-wrap *ngIf="tagsEnabled && post.tags && post.tags.length > 0">
|
||||||
|
<div item-start>{{ 'core.tag.tags' | translate }}:</div>
|
||||||
|
<core-tag-list [tags]="post.tags"></core-tag-list>
|
||||||
|
</ion-item>
|
||||||
<core-rating-rate *ngIf="forum && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="componentId" [itemId]="post.id" [itemSetId]="discussionId" [courseId]="courseId" [aggregateMethod]="forum.assessed" [scaleId]="forum.scale" [userId]="post.userid" (onUpdate)="ratingUpdated()"></core-rating-rate>
|
<core-rating-rate *ngIf="forum && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="componentId" [itemId]="post.id" [itemSetId]="discussionId" [courseId]="courseId" [aggregateMethod]="forum.assessed" [scaleId]="forum.scale" [userId]="post.userid" (onUpdate)="ratingUpdated()"></core-rating-rate>
|
||||||
<core-rating-aggregate *ngIf="forum && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="componentId" [itemId]="post.id" [courseId]="courseId" [aggregateMethod]="forum.assessed" [scaleId]="forum.scale"></core-rating-aggregate>
|
<core-rating-aggregate *ngIf="forum && ratingInfo" [ratingInfo]="ratingInfo" contextLevel="module" [instanceId]="componentId" [itemId]="post.id" [courseId]="courseId" [aggregateMethod]="forum.assessed" [scaleId]="forum.scale"></core-rating-aggregate>
|
||||||
<ion-item no-padding text-end *ngIf="post.id && post.canreply && !post.isprivatereply" class="addon-forum-reply-button">
|
<ion-item no-padding text-end *ngIf="post.id && post.canreply && !post.isprivatereply" class="addon-forum-reply-button">
|
||||||
|
|
|
@ -25,6 +25,7 @@ import { AddonModForumHelperProvider } from '../../providers/helper';
|
||||||
import { AddonModForumOfflineProvider } from '../../providers/offline';
|
import { AddonModForumOfflineProvider } from '../../providers/offline';
|
||||||
import { AddonModForumSyncProvider } from '../../providers/sync';
|
import { AddonModForumSyncProvider } from '../../providers/sync';
|
||||||
import { CoreRatingInfo } from '@core/rating/providers/rating';
|
import { CoreRatingInfo } from '@core/rating/providers/rating';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.).
|
* Components that shows a discussion post, its attachments and the action buttons allowed (reply, etc.).
|
||||||
|
@ -52,6 +53,7 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
uniqueId: string;
|
uniqueId: string;
|
||||||
advanced = false; // Display all form fields.
|
advanced = false; // Display all form fields.
|
||||||
|
tagsEnabled: boolean;
|
||||||
|
|
||||||
protected syncId: string;
|
protected syncId: string;
|
||||||
|
|
||||||
|
@ -65,8 +67,10 @@ export class AddonModForumPostComponent implements OnInit, OnDestroy {
|
||||||
private forumHelper: AddonModForumHelperProvider,
|
private forumHelper: AddonModForumHelperProvider,
|
||||||
private forumOffline: AddonModForumOfflineProvider,
|
private forumOffline: AddonModForumOfflineProvider,
|
||||||
private forumSync: AddonModForumSyncProvider,
|
private forumSync: AddonModForumSyncProvider,
|
||||||
|
private tagProvider: CoreTagProvider,
|
||||||
@Optional() private content: Content) {
|
@Optional() private content: Content) {
|
||||||
this.onPostChange = new EventEmitter<void>();
|
this.onPostChange = new EventEmitter<void>();
|
||||||
|
this.tagsEnabled = this.tagProvider.areTagsAvailableInSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate
|
||||||
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
||||||
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||||
import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate';
|
import { CorePushNotificationsDelegate } from '@core/pushnotifications/providers/delegate';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
import { AddonModForumProvider } from './providers/forum';
|
import { AddonModForumProvider } from './providers/forum';
|
||||||
import { AddonModForumOfflineProvider } from './providers/offline';
|
import { AddonModForumOfflineProvider } from './providers/offline';
|
||||||
import { AddonModForumHelperProvider } from './providers/helper';
|
import { AddonModForumHelperProvider } from './providers/helper';
|
||||||
|
@ -30,6 +31,7 @@ import { AddonModForumDiscussionLinkHandler } from './providers/discussion-link-
|
||||||
import { AddonModForumListLinkHandler } from './providers/list-link-handler';
|
import { AddonModForumListLinkHandler } from './providers/list-link-handler';
|
||||||
import { AddonModForumPostLinkHandler } from './providers/post-link-handler';
|
import { AddonModForumPostLinkHandler } from './providers/post-link-handler';
|
||||||
import { AddonModForumPushClickHandler } from './providers/push-click-handler';
|
import { AddonModForumPushClickHandler } from './providers/push-click-handler';
|
||||||
|
import { AddonModForumTagAreaHandler } from './providers/tag-area-handler';
|
||||||
import { AddonModForumComponentsModule } from './components/components.module';
|
import { AddonModForumComponentsModule } from './components/components.module';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
||||||
|
|
||||||
|
@ -59,7 +61,8 @@ export const ADDON_MOD_FORUM_PROVIDERS: any[] = [
|
||||||
AddonModForumListLinkHandler,
|
AddonModForumListLinkHandler,
|
||||||
AddonModForumPostLinkHandler,
|
AddonModForumPostLinkHandler,
|
||||||
AddonModForumDiscussionLinkHandler,
|
AddonModForumDiscussionLinkHandler,
|
||||||
AddonModForumPushClickHandler
|
AddonModForumPushClickHandler,
|
||||||
|
AddonModForumTagAreaHandler
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AddonModForumModule {
|
export class AddonModForumModule {
|
||||||
|
@ -69,7 +72,8 @@ export class AddonModForumModule {
|
||||||
indexHandler: AddonModForumIndexLinkHandler, discussionHandler: AddonModForumDiscussionLinkHandler,
|
indexHandler: AddonModForumIndexLinkHandler, discussionHandler: AddonModForumDiscussionLinkHandler,
|
||||||
updateManager: CoreUpdateManagerProvider, listLinkHandler: AddonModForumListLinkHandler,
|
updateManager: CoreUpdateManagerProvider, listLinkHandler: AddonModForumListLinkHandler,
|
||||||
pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModForumPushClickHandler,
|
pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModForumPushClickHandler,
|
||||||
postLinkHandler: AddonModForumPostLinkHandler) {
|
postLinkHandler: AddonModForumPostLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
||||||
|
tagAreaHandler: AddonModForumTagAreaHandler) {
|
||||||
|
|
||||||
moduleDelegate.registerHandler(moduleHandler);
|
moduleDelegate.registerHandler(moduleHandler);
|
||||||
prefetchDelegate.registerHandler(prefetchHandler);
|
prefetchDelegate.registerHandler(prefetchHandler);
|
||||||
|
@ -79,6 +83,7 @@ export class AddonModForumModule {
|
||||||
linksDelegate.registerHandler(listLinkHandler);
|
linksDelegate.registerHandler(listLinkHandler);
|
||||||
linksDelegate.registerHandler(postLinkHandler);
|
linksDelegate.registerHandler(postLinkHandler);
|
||||||
pushNotificationsDelegate.registerClickHandler(pushClickHandler);
|
pushNotificationsDelegate.registerClickHandler(pushClickHandler);
|
||||||
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
// Allow migrating the tables from the old app to the new schema.
|
||||||
updateManager.registerSiteTablesMigration([
|
updateManager.registerSiteTablesMigration([
|
||||||
|
|
|
@ -50,6 +50,7 @@
|
||||||
"reply": "Reply",
|
"reply": "Reply",
|
||||||
"replyplaceholder": "Write your reply...",
|
"replyplaceholder": "Write your reply...",
|
||||||
"subject": "Subject",
|
"subject": "Subject",
|
||||||
|
"tagarea_forum_posts": "Forum posts",
|
||||||
"thisforumhasduedate": "The due date for posting to this forum is {{$a}}.",
|
"thisforumhasduedate": "The due date for posting to this forum is {{$a}}.",
|
||||||
"thisforumisdue": "The due date for posting to this forum was {{$a}}.",
|
"thisforumisdue": "The due date for posting to this forum was {{$a}}.",
|
||||||
"unlockdiscussion": "Unlock this discussion",
|
"unlockdiscussion": "Unlock this discussion",
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreTagHelperProvider } from '@core/tag/providers/helper';
|
||||||
|
import { CoreTagFeedComponent } from '@core/tag/components/feed/feed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonModForumTagAreaHandler implements CoreTagAreaHandler {
|
||||||
|
name = 'AddonModForumTagAreaHandler';
|
||||||
|
type = 'mod_forum/forum_posts';
|
||||||
|
|
||||||
|
constructor(private tagHelper: CoreTagHelperProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]> {
|
||||||
|
return this.tagHelper.parseFeedContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any> {
|
||||||
|
return CoreTagFeedComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ import { CoreCronDelegate } from '@providers/cron';
|
||||||
import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
|
import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
|
||||||
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
||||||
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
import { AddonModGlossaryProvider } from './providers/glossary';
|
import { AddonModGlossaryProvider } from './providers/glossary';
|
||||||
import { AddonModGlossaryOfflineProvider } from './providers/offline';
|
import { AddonModGlossaryOfflineProvider } from './providers/offline';
|
||||||
import { AddonModGlossaryHelperProvider } from './providers/helper';
|
import { AddonModGlossaryHelperProvider } from './providers/helper';
|
||||||
|
@ -28,6 +29,7 @@ import { AddonModGlossaryIndexLinkHandler } from './providers/index-link-handler
|
||||||
import { AddonModGlossaryEntryLinkHandler } from './providers/entry-link-handler';
|
import { AddonModGlossaryEntryLinkHandler } from './providers/entry-link-handler';
|
||||||
import { AddonModGlossaryListLinkHandler } from './providers/list-link-handler';
|
import { AddonModGlossaryListLinkHandler } from './providers/list-link-handler';
|
||||||
import { AddonModGlossaryEditLinkHandler } from './providers/edit-link-handler';
|
import { AddonModGlossaryEditLinkHandler } from './providers/edit-link-handler';
|
||||||
|
import { AddonModGlossaryTagAreaHandler } from './providers/tag-area-handler';
|
||||||
import { AddonModGlossaryComponentsModule } from './components/components.module';
|
import { AddonModGlossaryComponentsModule } from './components/components.module';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
||||||
|
|
||||||
|
@ -56,7 +58,8 @@ export const ADDON_MOD_GLOSSARY_PROVIDERS: any[] = [
|
||||||
AddonModGlossaryIndexLinkHandler,
|
AddonModGlossaryIndexLinkHandler,
|
||||||
AddonModGlossaryEntryLinkHandler,
|
AddonModGlossaryEntryLinkHandler,
|
||||||
AddonModGlossaryListLinkHandler,
|
AddonModGlossaryListLinkHandler,
|
||||||
AddonModGlossaryEditLinkHandler
|
AddonModGlossaryEditLinkHandler,
|
||||||
|
AddonModGlossaryTagAreaHandler
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AddonModGlossaryModule {
|
export class AddonModGlossaryModule {
|
||||||
|
@ -65,7 +68,8 @@ export class AddonModGlossaryModule {
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonModGlossarySyncCronHandler, linksDelegate: CoreContentLinksDelegate,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonModGlossarySyncCronHandler, linksDelegate: CoreContentLinksDelegate,
|
||||||
indexHandler: AddonModGlossaryIndexLinkHandler, discussionHandler: AddonModGlossaryEntryLinkHandler,
|
indexHandler: AddonModGlossaryIndexLinkHandler, discussionHandler: AddonModGlossaryEntryLinkHandler,
|
||||||
updateManager: CoreUpdateManagerProvider, listLinkHandler: AddonModGlossaryListLinkHandler,
|
updateManager: CoreUpdateManagerProvider, listLinkHandler: AddonModGlossaryListLinkHandler,
|
||||||
editLinkHandler: AddonModGlossaryEditLinkHandler) {
|
editLinkHandler: AddonModGlossaryEditLinkHandler, tagAreaDelegate: CoreTagAreaDelegate,
|
||||||
|
tagAreaHandler: AddonModGlossaryTagAreaHandler) {
|
||||||
|
|
||||||
moduleDelegate.registerHandler(moduleHandler);
|
moduleDelegate.registerHandler(moduleHandler);
|
||||||
prefetchDelegate.registerHandler(prefetchHandler);
|
prefetchDelegate.registerHandler(prefetchHandler);
|
||||||
|
@ -74,6 +78,7 @@ export class AddonModGlossaryModule {
|
||||||
linksDelegate.registerHandler(discussionHandler);
|
linksDelegate.registerHandler(discussionHandler);
|
||||||
linksDelegate.registerHandler(listLinkHandler);
|
linksDelegate.registerHandler(listLinkHandler);
|
||||||
linksDelegate.registerHandler(editLinkHandler);
|
linksDelegate.registerHandler(editLinkHandler);
|
||||||
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
// Allow migrating the tables from the old app to the new schema.
|
||||||
updateManager.registerSiteTableMigration({
|
updateManager.registerSiteTableMigration({
|
||||||
|
|
|
@ -26,5 +26,6 @@
|
||||||
"linking": "Auto-linking",
|
"linking": "Auto-linking",
|
||||||
"modulenameplural": "Glossaries",
|
"modulenameplural": "Glossaries",
|
||||||
"noentriesfound": "No entries were found.",
|
"noentriesfound": "No entries were found.",
|
||||||
"searchquery": "Search query"
|
"searchquery": "Search query",
|
||||||
|
"tagarea_glossary_entries": "Glossary entries"
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,10 @@
|
||||||
<core-file *ngFor="let file of entry.attachments" [file]="file" [component]="component" [componentId]="componentId"></core-file>
|
<core-file *ngFor="let file of entry.attachments" [file]="file" [component]="component" [componentId]="componentId"></core-file>
|
||||||
</div>
|
</div>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
<ion-item text-wrap *ngIf="tagsEnabled && entry && entry.tags && entry.tags.length > 0">
|
||||||
|
<div item-start>{{ 'core.tag.tags' | translate }}:</div>
|
||||||
|
<core-tag-list [tags]="entry.tags"></core-tag-list>
|
||||||
|
</ion-item>
|
||||||
<ion-item text-wrap *ngIf="entry.approved != 1">
|
<ion-item text-wrap *ngIf="entry.approved != 1">
|
||||||
<p><em>{{ 'addon.mod_glossary.entrypendingapproval' | translate }}</em></p>
|
<p><em>{{ 'addon.mod_glossary.entrypendingapproval' | translate }}</em></p>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
|
|
|
@ -19,6 +19,7 @@ import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
import { CoreRatingComponentsModule } from '@core/rating/components/components.module';
|
import { CoreRatingComponentsModule } from '@core/rating/components/components.module';
|
||||||
|
import { CoreTagComponentsModule } from '@core/tag/components/components.module';
|
||||||
import { AddonModGlossaryEntryPage } from './entry';
|
import { AddonModGlossaryEntryPage } from './entry';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -31,7 +32,8 @@ import { AddonModGlossaryEntryPage } from './entry';
|
||||||
CorePipesModule,
|
CorePipesModule,
|
||||||
IonicPageModule.forChild(AddonModGlossaryEntryPage),
|
IonicPageModule.forChild(AddonModGlossaryEntryPage),
|
||||||
TranslateModule.forChild(),
|
TranslateModule.forChild(),
|
||||||
CoreRatingComponentsModule
|
CoreRatingComponentsModule,
|
||||||
|
CoreTagComponentsModule
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AddonModForumDiscussionPageModule {}
|
export class AddonModForumDiscussionPageModule {}
|
||||||
|
|
|
@ -16,6 +16,7 @@ import { Component } from '@angular/core';
|
||||||
import { IonicPage, NavParams } from 'ionic-angular';
|
import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
import { CoreRatingInfo } from '@core/rating/providers/rating';
|
import { CoreRatingInfo } from '@core/rating/providers/rating';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
import { AddonModGlossaryProvider } from '../../providers/glossary';
|
import { AddonModGlossaryProvider } from '../../providers/glossary';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,15 +36,18 @@ export class AddonModGlossaryEntryPage {
|
||||||
showAuthor = false;
|
showAuthor = false;
|
||||||
showDate = false;
|
showDate = false;
|
||||||
ratingInfo: CoreRatingInfo;
|
ratingInfo: CoreRatingInfo;
|
||||||
|
tagsEnabled: boolean;
|
||||||
|
|
||||||
protected courseId: number;
|
protected courseId: number;
|
||||||
protected entryId: number;
|
protected entryId: number;
|
||||||
|
|
||||||
constructor(navParams: NavParams,
|
constructor(navParams: NavParams,
|
||||||
private domUtils: CoreDomUtilsProvider,
|
private domUtils: CoreDomUtilsProvider,
|
||||||
private glossaryProvider: AddonModGlossaryProvider) {
|
private glossaryProvider: AddonModGlossaryProvider,
|
||||||
|
private tagProvider: CoreTagProvider) {
|
||||||
this.courseId = navParams.get('courseId');
|
this.courseId = navParams.get('courseId');
|
||||||
this.entryId = navParams.get('entryId');
|
this.entryId = navParams.get('entryId');
|
||||||
|
this.tagsEnabled = this.tagProvider.areTagsAvailableInSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreTagHelperProvider } from '@core/tag/providers/helper';
|
||||||
|
import { CoreTagFeedComponent } from '@core/tag/components/feed/feed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonModGlossaryTagAreaHandler implements CoreTagAreaHandler {
|
||||||
|
name = 'AddonModGlossaryTagAreaHandler';
|
||||||
|
type = 'mod_glossary/glossary_entries';
|
||||||
|
|
||||||
|
constructor(private tagHelper: CoreTagHelperProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]> {
|
||||||
|
return this.tagHelper.parseFeedContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any> {
|
||||||
|
return CoreTagFeedComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
|
import { CoreCourseComponentsModule } from '@core/course/components/components.module';
|
||||||
|
import { CoreTagComponentsModule } from '@core/tag/components/components.module';
|
||||||
import { AddonModWikiIndexComponent } from './index/index';
|
import { AddonModWikiIndexComponent } from './index/index';
|
||||||
import { AddonModWikiSubwikiPickerComponent } from './subwiki-picker/subwiki-picker';
|
import { AddonModWikiSubwikiPickerComponent } from './subwiki-picker/subwiki-picker';
|
||||||
|
|
||||||
|
@ -33,7 +34,8 @@ import { AddonModWikiSubwikiPickerComponent } from './subwiki-picker/subwiki-pic
|
||||||
TranslateModule.forChild(),
|
TranslateModule.forChild(),
|
||||||
CoreComponentsModule,
|
CoreComponentsModule,
|
||||||
CoreDirectivesModule,
|
CoreDirectivesModule,
|
||||||
CoreCourseComponentsModule
|
CoreCourseComponentsModule,
|
||||||
|
CoreTagComponentsModule
|
||||||
],
|
],
|
||||||
providers: [
|
providers: [
|
||||||
],
|
],
|
||||||
|
|
|
@ -50,6 +50,11 @@
|
||||||
<core-format-text *ngIf="pageContent" [component]="component" [componentId]="componentId" [text]="pageContent"></core-format-text>
|
<core-format-text *ngIf="pageContent" [component]="component" [componentId]="componentId" [text]="pageContent"></core-format-text>
|
||||||
<core-empty-box *ngIf="!pageContent" icon="document" [message]="'addon.mod_wiki.nocontent' | translate" [inline]="true"></core-empty-box>
|
<core-empty-box *ngIf="!pageContent" icon="document" [message]="'addon.mod_wiki.nocontent' | translate" [inline]="true"></core-empty-box>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
<div margin-top *ngIf="tagsEnabled && currentPageObj && currentPageObj.tags && currentPageObj.tags.length > 0">
|
||||||
|
<b>{{ 'core.tag.tags' | translate }}:</b>
|
||||||
|
<core-tag-list [tags]="currentPageObj.tags"></core-tag-list>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</ng-template>
|
</ng-template>
|
||||||
</core-tab>
|
</core-tab>
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { AddonModWikiOfflineProvider } from '../../providers/wiki-offline';
|
||||||
import { AddonModWikiSyncProvider } from '../../providers/wiki-sync';
|
import { AddonModWikiSyncProvider } from '../../providers/wiki-sync';
|
||||||
import { CoreTabsComponent } from '@components/tabs/tabs';
|
import { CoreTabsComponent } from '@components/tabs/tabs';
|
||||||
import { AddonModWikiSubwikiPickerComponent } from '../../components/subwiki-picker/subwiki-picker';
|
import { AddonModWikiSubwikiPickerComponent } from '../../components/subwiki-picker/subwiki-picker';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component that displays a wiki entry page.
|
* Component that displays a wiki entry page.
|
||||||
|
@ -64,6 +65,7 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
subwikis: [],
|
subwikis: [],
|
||||||
count: 0
|
count: 0
|
||||||
};
|
};
|
||||||
|
tagsEnabled: boolean;
|
||||||
|
|
||||||
protected syncEventName = AddonModWikiSyncProvider.AUTO_SYNCED;
|
protected syncEventName = AddonModWikiSyncProvider.AUTO_SYNCED;
|
||||||
protected currentSubwiki: any; // Current selected subwiki.
|
protected currentSubwiki: any; // Current selected subwiki.
|
||||||
|
@ -81,10 +83,12 @@ export class AddonModWikiIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
constructor(injector: Injector, protected wikiProvider: AddonModWikiProvider, @Optional() protected content: Content,
|
constructor(injector: Injector, protected wikiProvider: AddonModWikiProvider, @Optional() protected content: Content,
|
||||||
protected wikiOffline: AddonModWikiOfflineProvider, protected wikiSync: AddonModWikiSyncProvider,
|
protected wikiOffline: AddonModWikiOfflineProvider, protected wikiSync: AddonModWikiSyncProvider,
|
||||||
protected navCtrl: NavController, protected utils: CoreUtilsProvider, protected groupsProvider: CoreGroupsProvider,
|
protected navCtrl: NavController, protected utils: CoreUtilsProvider, protected groupsProvider: CoreGroupsProvider,
|
||||||
protected userProvider: CoreUserProvider, private popoverCtrl: PopoverController) {
|
protected userProvider: CoreUserProvider, private popoverCtrl: PopoverController,
|
||||||
|
private tagProvider: CoreTagProvider) {
|
||||||
super(injector, content);
|
super(injector, content);
|
||||||
|
|
||||||
this.pageStr = this.translate.instant('addon.mod_wiki.wikipage');
|
this.pageStr = this.translate.instant('addon.mod_wiki.wikipage');
|
||||||
|
this.tagsEnabled = this.tagProvider.areTagsAvailableInSite();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
"pageexists": "This page already exists.",
|
"pageexists": "This page already exists.",
|
||||||
"pagename": "Page name",
|
"pagename": "Page name",
|
||||||
"subwiki": "Sub-wiki",
|
"subwiki": "Sub-wiki",
|
||||||
|
"tagarea_wiki_pages": "Wiki pages",
|
||||||
"titleshouldnotbeempty": "The title should not be empty",
|
"titleshouldnotbeempty": "The title should not be empty",
|
||||||
"viewpage": "View page",
|
"viewpage": "View page",
|
||||||
"wikipage": "Wiki page",
|
"wikipage": "Wiki page",
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreTagHelperProvider } from '@core/tag/providers/helper';
|
||||||
|
import { CoreTagFeedComponent } from '@core/tag/components/feed/feed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class AddonModWikiTagAreaHandler implements CoreTagAreaHandler {
|
||||||
|
name = 'AddonModWikiTagAreaHandler';
|
||||||
|
type = 'mod_wiki/wiki_pages';
|
||||||
|
|
||||||
|
constructor(private tagHelper: CoreTagHelperProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]> {
|
||||||
|
return this.tagHelper.parseFeedContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any> {
|
||||||
|
return CoreTagFeedComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ import { CoreCronDelegate } from '@providers/cron';
|
||||||
import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
|
import { CoreCourseModuleDelegate } from '@core/course/providers/module-delegate';
|
||||||
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
import { CoreCourseModulePrefetchDelegate } from '@core/course/providers/module-prefetch-delegate';
|
||||||
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
import { AddonModWikiComponentsModule } from './components/components.module';
|
import { AddonModWikiComponentsModule } from './components/components.module';
|
||||||
import { AddonModWikiProvider } from './providers/wiki';
|
import { AddonModWikiProvider } from './providers/wiki';
|
||||||
import { AddonModWikiOfflineProvider } from './providers/wiki-offline';
|
import { AddonModWikiOfflineProvider } from './providers/wiki-offline';
|
||||||
|
@ -29,6 +30,7 @@ import { AddonModWikiPageOrMapLinkHandler } from './providers/page-or-map-link-h
|
||||||
import { AddonModWikiCreateLinkHandler } from './providers/create-link-handler';
|
import { AddonModWikiCreateLinkHandler } from './providers/create-link-handler';
|
||||||
import { AddonModWikiEditLinkHandler } from './providers/edit-link-handler';
|
import { AddonModWikiEditLinkHandler } from './providers/edit-link-handler';
|
||||||
import { AddonModWikiListLinkHandler } from './providers/list-link-handler';
|
import { AddonModWikiListLinkHandler } from './providers/list-link-handler';
|
||||||
|
import { AddonModWikiTagAreaHandler } from './providers/tag-area-handler';
|
||||||
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
import { CoreUpdateManagerProvider } from '@providers/update-manager';
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
|
@ -55,7 +57,8 @@ export const ADDON_MOD_WIKI_PROVIDERS: any[] = [
|
||||||
AddonModWikiPageOrMapLinkHandler,
|
AddonModWikiPageOrMapLinkHandler,
|
||||||
AddonModWikiCreateLinkHandler,
|
AddonModWikiCreateLinkHandler,
|
||||||
AddonModWikiEditLinkHandler,
|
AddonModWikiEditLinkHandler,
|
||||||
AddonModWikiListLinkHandler
|
AddonModWikiListLinkHandler,
|
||||||
|
AddonModWikiTagAreaHandler
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class AddonModWikiModule {
|
export class AddonModWikiModule {
|
||||||
|
@ -64,7 +67,8 @@ export class AddonModWikiModule {
|
||||||
cronDelegate: CoreCronDelegate, syncHandler: AddonModWikiSyncCronHandler, linksDelegate: CoreContentLinksDelegate,
|
cronDelegate: CoreCronDelegate, syncHandler: AddonModWikiSyncCronHandler, linksDelegate: CoreContentLinksDelegate,
|
||||||
indexHandler: AddonModWikiIndexLinkHandler, pageOrMapHandler: AddonModWikiPageOrMapLinkHandler,
|
indexHandler: AddonModWikiIndexLinkHandler, pageOrMapHandler: AddonModWikiPageOrMapLinkHandler,
|
||||||
createHandler: AddonModWikiCreateLinkHandler, editHandler: AddonModWikiEditLinkHandler,
|
createHandler: AddonModWikiCreateLinkHandler, editHandler: AddonModWikiEditLinkHandler,
|
||||||
updateManager: CoreUpdateManagerProvider, listLinkHandler: AddonModWikiListLinkHandler) {
|
updateManager: CoreUpdateManagerProvider, listLinkHandler: AddonModWikiListLinkHandler,
|
||||||
|
tagAreaDelegate: CoreTagAreaDelegate, tagAreaHandler: AddonModWikiTagAreaHandler) {
|
||||||
|
|
||||||
moduleDelegate.registerHandler(moduleHandler);
|
moduleDelegate.registerHandler(moduleHandler);
|
||||||
prefetchDelegate.registerHandler(prefetchHandler);
|
prefetchDelegate.registerHandler(prefetchHandler);
|
||||||
|
@ -74,6 +78,7 @@ export class AddonModWikiModule {
|
||||||
linksDelegate.registerHandler(createHandler);
|
linksDelegate.registerHandler(createHandler);
|
||||||
linksDelegate.registerHandler(editHandler);
|
linksDelegate.registerHandler(editHandler);
|
||||||
linksDelegate.registerHandler(listLinkHandler);
|
linksDelegate.registerHandler(listLinkHandler);
|
||||||
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
|
|
||||||
// Allow migrating the tables from the old app to the new schema.
|
// Allow migrating the tables from the old app to the new schema.
|
||||||
updateManager.registerSiteTableMigration({
|
updateManager.registerSiteTableMigration({
|
||||||
|
|
|
@ -81,6 +81,7 @@ import { CoreQuestionModule } from '@core/question/question.module';
|
||||||
import { CoreCommentsModule } from '@core/comments/comments.module';
|
import { CoreCommentsModule } from '@core/comments/comments.module';
|
||||||
import { CoreBlockModule } from '@core/block/block.module';
|
import { CoreBlockModule } from '@core/block/block.module';
|
||||||
import { CoreRatingModule } from '@core/rating/rating.module';
|
import { CoreRatingModule } from '@core/rating/rating.module';
|
||||||
|
import { CoreTagModule } from '@core/tag/tag.module';
|
||||||
|
|
||||||
// Addon modules.
|
// Addon modules.
|
||||||
import { AddonBadgesModule } from '@addon/badges/badges.module';
|
import { AddonBadgesModule } from '@addon/badges/badges.module';
|
||||||
|
@ -225,6 +226,7 @@ export const WP_PROVIDER: any = null;
|
||||||
CoreBlockModule,
|
CoreBlockModule,
|
||||||
CoreRatingModule,
|
CoreRatingModule,
|
||||||
CorePushNotificationsModule,
|
CorePushNotificationsModule,
|
||||||
|
CoreTagModule,
|
||||||
AddonBadgesModule,
|
AddonBadgesModule,
|
||||||
AddonBlogModule,
|
AddonBlogModule,
|
||||||
AddonCalendarModule,
|
AddonCalendarModule,
|
||||||
|
|
|
@ -419,6 +419,7 @@
|
||||||
"addon.mod_assign_submission_onlinetext.wordlimitexceeded": "The word limit for this assignment is {{$a.limit}} words and you are attempting to submit {{$a.count}} words. Please review your submission and try again.",
|
"addon.mod_assign_submission_onlinetext.wordlimitexceeded": "The word limit for this assignment is {{$a.limit}} words and you are attempting to submit {{$a.count}} words. Please review your submission and try again.",
|
||||||
"addon.mod_book.errorchapter": "Error reading chapter of book.",
|
"addon.mod_book.errorchapter": "Error reading chapter of book.",
|
||||||
"addon.mod_book.modulenameplural": "Books",
|
"addon.mod_book.modulenameplural": "Books",
|
||||||
|
"addon.mod_book.tagarea_book_chapters": "Book chapters",
|
||||||
"addon.mod_book.toc": "Table of contents",
|
"addon.mod_book.toc": "Table of contents",
|
||||||
"addon.mod_chat.beep": "Beep",
|
"addon.mod_chat.beep": "Beep",
|
||||||
"addon.mod_chat.chatreport": "Chat sessions",
|
"addon.mod_chat.chatreport": "Chat sessions",
|
||||||
|
@ -478,6 +479,7 @@
|
||||||
"addon.mod_data.confirmdeleterecord": "Are you sure you want to delete this entry?",
|
"addon.mod_data.confirmdeleterecord": "Are you sure you want to delete this entry?",
|
||||||
"addon.mod_data.descending": "Descending",
|
"addon.mod_data.descending": "Descending",
|
||||||
"addon.mod_data.disapprove": "Undo approval",
|
"addon.mod_data.disapprove": "Undo approval",
|
||||||
|
"addon.mod_data.edittagsnotsupported": "Sorry, editing tags is not supported by the app.",
|
||||||
"addon.mod_data.emptyaddform": "You did not fill out any fields!",
|
"addon.mod_data.emptyaddform": "You did not fill out any fields!",
|
||||||
"addon.mod_data.entrieslefttoadd": "You must add {{$a.entriesleft}} more entry/entries in order to complete this activity",
|
"addon.mod_data.entrieslefttoadd": "You must add {{$a.entriesleft}} more entry/entries in order to complete this activity",
|
||||||
"addon.mod_data.entrieslefttoaddtoview": "You must add {{$a.entrieslefttoview}} more entry/entries before you can view other participants' entries.",
|
"addon.mod_data.entrieslefttoaddtoview": "You must add {{$a.entrieslefttoview}} more entry/entries before you can view other participants' entries.",
|
||||||
|
@ -502,8 +504,10 @@
|
||||||
"addon.mod_data.recorddisapproved": "Entry unapproved",
|
"addon.mod_data.recorddisapproved": "Entry unapproved",
|
||||||
"addon.mod_data.resetsettings": "Reset filters",
|
"addon.mod_data.resetsettings": "Reset filters",
|
||||||
"addon.mod_data.search": "Search",
|
"addon.mod_data.search": "Search",
|
||||||
|
"addon.mod_data.searchbytagsnotsupported": "Sorry, searching by tags is not supported by the app.",
|
||||||
"addon.mod_data.selectedrequired": "All selected required",
|
"addon.mod_data.selectedrequired": "All selected required",
|
||||||
"addon.mod_data.single": "View single",
|
"addon.mod_data.single": "View single",
|
||||||
|
"addon.mod_data.tagarea_data_records": "Data records",
|
||||||
"addon.mod_data.timeadded": "Time added",
|
"addon.mod_data.timeadded": "Time added",
|
||||||
"addon.mod_data.timemodified": "Time modified",
|
"addon.mod_data.timemodified": "Time modified",
|
||||||
"addon.mod_data.usedate": "Include in search.",
|
"addon.mod_data.usedate": "Include in search.",
|
||||||
|
@ -596,6 +600,7 @@
|
||||||
"addon.mod_forum.reply": "Reply",
|
"addon.mod_forum.reply": "Reply",
|
||||||
"addon.mod_forum.replyplaceholder": "Write your reply...",
|
"addon.mod_forum.replyplaceholder": "Write your reply...",
|
||||||
"addon.mod_forum.subject": "Subject",
|
"addon.mod_forum.subject": "Subject",
|
||||||
|
"addon.mod_forum.tagarea_forum_posts": "Forum posts",
|
||||||
"addon.mod_forum.thisforumhasduedate": "The due date for posting to this forum is {{$a}}.",
|
"addon.mod_forum.thisforumhasduedate": "The due date for posting to this forum is {{$a}}.",
|
||||||
"addon.mod_forum.thisforumisdue": "The due date for posting to this forum was {{$a}}.",
|
"addon.mod_forum.thisforumisdue": "The due date for posting to this forum was {{$a}}.",
|
||||||
"addon.mod_forum.unlockdiscussion": "Unlock this discussion",
|
"addon.mod_forum.unlockdiscussion": "Unlock this discussion",
|
||||||
|
@ -630,6 +635,7 @@
|
||||||
"addon.mod_glossary.modulenameplural": "Glossaries",
|
"addon.mod_glossary.modulenameplural": "Glossaries",
|
||||||
"addon.mod_glossary.noentriesfound": "No entries were found.",
|
"addon.mod_glossary.noentriesfound": "No entries were found.",
|
||||||
"addon.mod_glossary.searchquery": "Search query",
|
"addon.mod_glossary.searchquery": "Search query",
|
||||||
|
"addon.mod_glossary.tagarea_glossary_entries": "Glossary entries",
|
||||||
"addon.mod_imscp.deploymenterror": "Content package error!",
|
"addon.mod_imscp.deploymenterror": "Content package error!",
|
||||||
"addon.mod_imscp.modulenameplural": "IMS content packages",
|
"addon.mod_imscp.modulenameplural": "IMS content packages",
|
||||||
"addon.mod_imscp.showmoduledescription": "Show description",
|
"addon.mod_imscp.showmoduledescription": "Show description",
|
||||||
|
@ -886,6 +892,7 @@
|
||||||
"addon.mod_wiki.pageexists": "This page already exists.",
|
"addon.mod_wiki.pageexists": "This page already exists.",
|
||||||
"addon.mod_wiki.pagename": "Page name",
|
"addon.mod_wiki.pagename": "Page name",
|
||||||
"addon.mod_wiki.subwiki": "Sub-wiki",
|
"addon.mod_wiki.subwiki": "Sub-wiki",
|
||||||
|
"addon.mod_wiki.tagarea_wiki_pages": "Wiki pages",
|
||||||
"addon.mod_wiki.titleshouldnotbeempty": "The title should not be empty",
|
"addon.mod_wiki.titleshouldnotbeempty": "The title should not be empty",
|
||||||
"addon.mod_wiki.viewpage": "View page",
|
"addon.mod_wiki.viewpage": "View page",
|
||||||
"addon.mod_wiki.wikipage": "Wiki page",
|
"addon.mod_wiki.wikipage": "Wiki page",
|
||||||
|
@ -1863,6 +1870,20 @@
|
||||||
"core.submit": "Submit",
|
"core.submit": "Submit",
|
||||||
"core.success": "Success",
|
"core.success": "Success",
|
||||||
"core.tablet": "Tablet",
|
"core.tablet": "Tablet",
|
||||||
|
"core.tag.defautltagcoll": "Default collection",
|
||||||
|
"core.tag.errorareanotsupported": "This tag area is not supported by the app.",
|
||||||
|
"core.tag.inalltagcoll": "Everywhere",
|
||||||
|
"core.tag.itemstaggedwith": "{{$a.tagarea}} tagged with \"{{$a.tag}}\"",
|
||||||
|
"core.tag.notagsfound": "No tags matching \"{{$a}}\" found",
|
||||||
|
"core.tag.searchtags": "Search tags",
|
||||||
|
"core.tag.showingfirsttags": "Showing {{$a}} most popular tags",
|
||||||
|
"core.tag.tag": "Tag",
|
||||||
|
"core.tag.tagarea_course": "Courses",
|
||||||
|
"core.tag.tagarea_course_modules": "Activities and resources",
|
||||||
|
"core.tag.tagarea_post": "Blog posts",
|
||||||
|
"core.tag.tagarea_user": "User interests",
|
||||||
|
"core.tag.tags": "Tags",
|
||||||
|
"core.tag.warningareasnotsupported": "Some of the tag areas are not displayed because they are not supported by the app.",
|
||||||
"core.teachers": "Teachers",
|
"core.teachers": "Teachers",
|
||||||
"core.thereisdatatosync": "There are offline {{$a}} to be synchronised.",
|
"core.thereisdatatosync": "There are offline {{$a}} to be synchronised.",
|
||||||
"core.thisdirection": "ltr",
|
"core.thisdirection": "ltr",
|
||||||
|
|
|
@ -39,6 +39,7 @@ export class CoreSearchBoxComponent implements OnInit {
|
||||||
@Input() lengthCheck = 3; // Check value length before submit. If 0, any string will be submitted.
|
@Input() lengthCheck = 3; // Check value length before submit. If 0, any string will be submitted.
|
||||||
@Input() showClear = true; // Show/hide clear button.
|
@Input() showClear = true; // Show/hide clear button.
|
||||||
@Input() disabled = false; // Disables the input text.
|
@Input() disabled = false; // Disables the input text.
|
||||||
|
@Input() initialSearch: string; // Initial search text.
|
||||||
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the search form.
|
@Output() onSubmit: EventEmitter<string>; // Send data when submitting the search form.
|
||||||
@Output() onClear: EventEmitter<void>; // Send event when clearing the search form.
|
@Output() onClear: EventEmitter<void>; // Send event when clearing the search form.
|
||||||
|
|
||||||
|
@ -55,6 +56,7 @@ export class CoreSearchBoxComponent implements OnInit {
|
||||||
this.placeholder = this.placeholder || this.translate.instant('core.search');
|
this.placeholder = this.placeholder || this.translate.instant('core.search');
|
||||||
this.spellcheck = this.utils.isTrueOrOne(this.spellcheck);
|
this.spellcheck = this.utils.isTrueOrOne(this.spellcheck);
|
||||||
this.showClear = this.utils.isTrueOrOne(this.showClear);
|
this.showClear = this.utils.isTrueOrOne(this.showClear);
|
||||||
|
this.searchText = this.initialSearch || '';
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -23,6 +23,7 @@ import { CoreCourseFormatComponent } from './format/format';
|
||||||
import { CoreCourseModuleComponent } from './module/module';
|
import { CoreCourseModuleComponent } from './module/module';
|
||||||
import { CoreCourseModuleCompletionComponent } from './module-completion/module-completion';
|
import { CoreCourseModuleCompletionComponent } from './module-completion/module-completion';
|
||||||
import { CoreCourseModuleDescriptionComponent } from './module-description/module-description';
|
import { CoreCourseModuleDescriptionComponent } from './module-description/module-description';
|
||||||
|
import { CoreCourseTagAreaComponent } from './tag-area/tag-area';
|
||||||
import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsupported-module';
|
import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsupported-module';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
|
@ -31,6 +32,7 @@ import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsup
|
||||||
CoreCourseModuleComponent,
|
CoreCourseModuleComponent,
|
||||||
CoreCourseModuleCompletionComponent,
|
CoreCourseModuleCompletionComponent,
|
||||||
CoreCourseModuleDescriptionComponent,
|
CoreCourseModuleDescriptionComponent,
|
||||||
|
CoreCourseTagAreaComponent,
|
||||||
CoreCourseUnsupportedModuleComponent
|
CoreCourseUnsupportedModuleComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -48,10 +50,12 @@ import { CoreCourseUnsupportedModuleComponent } from './unsupported-module/unsup
|
||||||
CoreCourseModuleComponent,
|
CoreCourseModuleComponent,
|
||||||
CoreCourseModuleCompletionComponent,
|
CoreCourseModuleCompletionComponent,
|
||||||
CoreCourseModuleDescriptionComponent,
|
CoreCourseModuleDescriptionComponent,
|
||||||
|
CoreCourseTagAreaComponent,
|
||||||
CoreCourseUnsupportedModuleComponent
|
CoreCourseUnsupportedModuleComponent
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
CoreCourseUnsupportedModuleComponent
|
CoreCourseUnsupportedModuleComponent,
|
||||||
|
CoreCourseTagAreaComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreCourseComponentsModule {}
|
export class CoreCourseComponentsModule {}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
<a ion-item text-wrap *ngFor="let item of items" (click)="openCourse(item.courseId)" [title]="item.courseName">
|
||||||
|
<core-icon name="fa-graduation-cap" item-start></core-icon>
|
||||||
|
<h2>{{ item.courseName }}</h2>
|
||||||
|
<p *ngIf="item.categoryName">{{ 'core.category' | translate }}: {{ item.categoryName }}</p>
|
||||||
|
</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, Optional } from '@angular/core';
|
||||||
|
import { NavController } from 'ionic-angular';
|
||||||
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
import { CoreCourseHelperProvider } from '@core/course/providers/helper';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that renders the course tag area.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-course-tag-area',
|
||||||
|
templateUrl: 'core-course-tag-area.html'
|
||||||
|
})
|
||||||
|
export class CoreCourseTagAreaComponent {
|
||||||
|
@Input() items: any[]; // Area items to render.
|
||||||
|
|
||||||
|
constructor(private navCtrl: NavController, @Optional() private splitviewCtrl: CoreSplitViewComponent,
|
||||||
|
private courseHelper: CoreCourseHelperProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Open a course.
|
||||||
|
*
|
||||||
|
* @param {number} courseId The course to open.
|
||||||
|
*/
|
||||||
|
openCourse(courseId: number): void {
|
||||||
|
// If this component is inside a split view, use the master nav to open it.
|
||||||
|
const navCtrl = this.splitviewCtrl ? this.splitviewCtrl.getMasterNav() : this.navCtrl;
|
||||||
|
this.courseHelper.getAndOpenCourse(navCtrl, courseId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,9 @@ import { CoreCourseFormatWeeksModule } from './formats/weeks/weeks.module';
|
||||||
import { CoreCourseSyncProvider } from './providers/sync';
|
import { CoreCourseSyncProvider } from './providers/sync';
|
||||||
import { CoreCourseSyncCronHandler } from './providers/sync-cron-handler';
|
import { CoreCourseSyncCronHandler } from './providers/sync-cron-handler';
|
||||||
import { CoreCourseLogCronHandler } from './providers/log-cron-handler';
|
import { CoreCourseLogCronHandler } from './providers/log-cron-handler';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreCourseTagAreaHandler } from './providers/course-tag-area-handler';
|
||||||
|
import { CoreCourseModulesTagAreaHandler } from './providers/modules-tag-area-handler';
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const CORE_COURSE_PROVIDERS: any[] = [
|
export const CORE_COURSE_PROVIDERS: any[] = [
|
||||||
|
@ -68,15 +71,20 @@ export const CORE_COURSE_PROVIDERS: any[] = [
|
||||||
CoreCourseFormatDefaultHandler,
|
CoreCourseFormatDefaultHandler,
|
||||||
CoreCourseModuleDefaultHandler,
|
CoreCourseModuleDefaultHandler,
|
||||||
CoreCourseSyncCronHandler,
|
CoreCourseSyncCronHandler,
|
||||||
CoreCourseLogCronHandler
|
CoreCourseLogCronHandler,
|
||||||
|
CoreCourseTagAreaHandler,
|
||||||
|
CoreCourseModulesTagAreaHandler
|
||||||
],
|
],
|
||||||
exports: []
|
exports: []
|
||||||
})
|
})
|
||||||
export class CoreCourseModule {
|
export class CoreCourseModule {
|
||||||
constructor(cronDelegate: CoreCronDelegate, syncHandler: CoreCourseSyncCronHandler, logHandler: CoreCourseLogCronHandler,
|
constructor(cronDelegate: CoreCronDelegate, syncHandler: CoreCourseSyncCronHandler, logHandler: CoreCourseLogCronHandler,
|
||||||
platform: Platform, eventsProvider: CoreEventsProvider) {
|
platform: Platform, eventsProvider: CoreEventsProvider, tagAreaDelegate: CoreTagAreaDelegate,
|
||||||
|
courseTagAreaHandler: CoreCourseTagAreaHandler, modulesTagAreaHandler: CoreCourseModulesTagAreaHandler) {
|
||||||
cronDelegate.register(syncHandler);
|
cronDelegate.register(syncHandler);
|
||||||
cronDelegate.register(logHandler);
|
cronDelegate.register(logHandler);
|
||||||
|
tagAreaDelegate.registerHandler(courseTagAreaHandler);
|
||||||
|
tagAreaDelegate.registerHandler(modulesTagAreaHandler);
|
||||||
|
|
||||||
platform.resume.subscribe(() => {
|
platform.resume.subscribe(() => {
|
||||||
// Log the app is open to keep user in online status.
|
// Log the app is open to keep user in online status.
|
||||||
|
|
|
@ -0,0 +1,74 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
import { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreCourseTagAreaComponent } from '../components/tag-area/tag-area';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreCourseTagAreaHandler implements CoreTagAreaHandler {
|
||||||
|
name = 'CoreCourseTagAreaHandler';
|
||||||
|
type = 'core/course';
|
||||||
|
|
||||||
|
constructor(private domUtils: CoreDomUtilsProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]> {
|
||||||
|
const items = [];
|
||||||
|
const element = this.domUtils.convertToElement(content);
|
||||||
|
|
||||||
|
Array.from(element.querySelectorAll('div.coursebox')).forEach((coursebox) => {
|
||||||
|
const courseId = parseInt(coursebox.getAttribute('data-courseid'), 10);
|
||||||
|
const courseLink = coursebox.querySelector('.coursename > a');
|
||||||
|
const categoryLink = coursebox.querySelector('.coursecat > a');
|
||||||
|
|
||||||
|
if (courseId > 0 && courseLink) {
|
||||||
|
items.push({
|
||||||
|
courseId,
|
||||||
|
courseName: courseLink.innerHTML,
|
||||||
|
categoryName: categoryLink ? categoryLink.innerHTML : null
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any> {
|
||||||
|
return CoreCourseTagAreaComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreTagHelperProvider } from '@core/tag/providers/helper';
|
||||||
|
import { CoreTagFeedComponent } from '@core/tag/components/feed/feed';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreCourseModulesTagAreaHandler implements CoreTagAreaHandler {
|
||||||
|
name = 'CoreCourseModulesTagAreaHandler';
|
||||||
|
type = 'core/course_modules';
|
||||||
|
|
||||||
|
constructor(protected tagHelper: CoreTagHelperProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]> {
|
||||||
|
return this.tagHelper.parseFeedContent(content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any> {
|
||||||
|
return CoreTagFeedComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// (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 { CoreTagFeedComponent } from './feed/feed';
|
||||||
|
import { CoreTagListComponent } from './list/list';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
CoreTagFeedComponent,
|
||||||
|
CoreTagListComponent
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
IonicModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
],
|
||||||
|
exports: [
|
||||||
|
CoreTagFeedComponent,
|
||||||
|
CoreTagListComponent
|
||||||
|
],
|
||||||
|
entryComponents: [
|
||||||
|
CoreTagFeedComponent
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CoreTagComponentsModule {}
|
|
@ -0,0 +1,8 @@
|
||||||
|
<a ion-item text-wrap *ngFor="let item of items" [href]="item.url" core-link [capture]="true">
|
||||||
|
<ion-avatar item-start *ngIf="item.avatarUrl">
|
||||||
|
<img [src]="item.avatarUrl" core-external-content alt="" role="presentation" onError="this.src='assets/img/user-avatar.png'">
|
||||||
|
</ion-avatar>
|
||||||
|
<img item-start *ngIf="item.iconUrl" [src]="item.iconUrl" core-external-content alt="" role="presentation" class="core-module-icon">
|
||||||
|
<h2>{{ item.heading }}</h2>
|
||||||
|
<p *ngFor="let text of item.details">{{ text }}</p>
|
||||||
|
</a>
|
|
@ -0,0 +1,26 @@
|
||||||
|
// (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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to render a tag area that uses the "core_tag/tagfeed" web template.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-tag-feed',
|
||||||
|
templateUrl: 'core-tag-feed.html'
|
||||||
|
})
|
||||||
|
export class CoreTagFeedComponent {
|
||||||
|
@Input() items: any[]; // Area items to render.
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
<ng-container *ngFor="let tag of tags">
|
||||||
|
<ion-badge (click)="openTag(tag)" class="core-tag-list-tag">{{ tag.rawname }}</ion-badge>
|
||||||
|
</ng-container>
|
|
@ -0,0 +1,7 @@
|
||||||
|
ion-app.app-root core-tag-list {
|
||||||
|
line-height: 1.6;
|
||||||
|
|
||||||
|
ion-badge {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
// (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, Optional } from '@angular/core';
|
||||||
|
import { NavController } from 'ionic-angular';
|
||||||
|
import { CoreTagItem } from '@core/tag/providers/tag';
|
||||||
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component that displays the list of tags of an item.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-tag-list',
|
||||||
|
templateUrl: 'core-tag-list.html'
|
||||||
|
})
|
||||||
|
export class CoreTagListComponent {
|
||||||
|
@Input() tags: CoreTagItem[];
|
||||||
|
|
||||||
|
constructor(private navCtrl: NavController, @Optional() private svComponent: CoreSplitViewComponent) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go to tag index page.
|
||||||
|
*/
|
||||||
|
openTag(tag: CoreTagItem): void {
|
||||||
|
const navCtrl = this.svComponent ? this.svComponent.getMasterNav() : this.navCtrl;
|
||||||
|
const params = {
|
||||||
|
tagId: tag.id,
|
||||||
|
tagName: tag.rawname,
|
||||||
|
collectionId: tag.tagcollid,
|
||||||
|
fromContextId: tag.taginstancecontextid
|
||||||
|
};
|
||||||
|
navCtrl.push('CoreTagIndexPage', params);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"defautltagcoll": "Default collection",
|
||||||
|
"errorareanotsupported": "This tag area is not supported by the app.",
|
||||||
|
"inalltagcoll": "Everywhere",
|
||||||
|
"itemstaggedwith": "{{$a.tagarea}} tagged with \"{{$a.tag}}\"",
|
||||||
|
"notagsfound": "No tags matching \"{{$a}}\" found",
|
||||||
|
"searchtags": "Search tags",
|
||||||
|
"showingfirsttags": "Showing {{$a}} most popular tags",
|
||||||
|
"tag": "Tag",
|
||||||
|
"tagarea_course": "Courses",
|
||||||
|
"tagarea_course_modules": "Activities and resources",
|
||||||
|
"tagarea_post": "Blog posts",
|
||||||
|
"tagarea_user": "User interests",
|
||||||
|
"tags": "Tags",
|
||||||
|
"warningareasnotsupported": "Some of the tag areas are not displayed because they are not supported by the app."
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar core-back-button>
|
||||||
|
<ion-title>{{ 'core.tag.itemstaggedwith' | translate: { $a: {tagarea: areaNameKey | translate, tag: tagName} } }}</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshData($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="loaded">
|
||||||
|
<ng-container *ngIf="loaded">
|
||||||
|
<core-dynamic-component [component]="areaComponent" [data]="{items: items}"></core-dynamic-component>
|
||||||
|
</ng-container>
|
||||||
|
<core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError"></core-infinite-loading>
|
||||||
|
</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 { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { CoreTagIndexAreaPage } from './index-area';
|
||||||
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
CoreTagIndexAreaPage
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
IonicPageModule.forChild(CoreTagIndexAreaPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CoreTagIndexAreaPageModule {}
|
|
@ -0,0 +1,150 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays the tag index area.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'core-tag-index-area' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-core-tag-index-area',
|
||||||
|
templateUrl: 'index-area.html',
|
||||||
|
})
|
||||||
|
export class CoreTagIndexAreaPage {
|
||||||
|
tagId: number;
|
||||||
|
tagName: string;
|
||||||
|
collectionId: number;
|
||||||
|
areaId: number;
|
||||||
|
fromContextId: number;
|
||||||
|
contextId: number;
|
||||||
|
recursive: boolean;
|
||||||
|
areaNameKey: string;
|
||||||
|
loaded = false;
|
||||||
|
componentName: string;
|
||||||
|
itemType: string;
|
||||||
|
items = [];
|
||||||
|
nextPage = 0;
|
||||||
|
canLoadMore = false;
|
||||||
|
areaComponent: any;
|
||||||
|
loadMoreError = false;
|
||||||
|
|
||||||
|
constructor(navParams: NavParams, private injector: Injector, private translate: TranslateService,
|
||||||
|
private tagProvider: CoreTagProvider, private domUtils: CoreDomUtilsProvider,
|
||||||
|
private tagAreaDelegate: CoreTagAreaDelegate) {
|
||||||
|
this.tagId = navParams.get('tagId');
|
||||||
|
this.tagName = navParams.get('tagName');
|
||||||
|
this.collectionId = navParams.get('collectionId');
|
||||||
|
this.areaId = navParams.get('areaId');
|
||||||
|
this.fromContextId = navParams.get('fromContextId');
|
||||||
|
this.contextId = navParams.get('contextId');
|
||||||
|
this.recursive = navParams.get('recursive');
|
||||||
|
this.areaNameKey = navParams.get('areaNameKey');
|
||||||
|
|
||||||
|
// Pass the the following parameters to avoid fetching the first page.
|
||||||
|
this.componentName = navParams.get('componentName');
|
||||||
|
this.itemType = navParams.get('itemType');
|
||||||
|
this.items = navParams.get('items') || [];
|
||||||
|
this.nextPage = navParams.get('nextPage') || 0;
|
||||||
|
this.canLoadMore = !!navParams.get('canLoadMore');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ionViewDidLoad(): void {
|
||||||
|
let promise: Promise<any>;
|
||||||
|
if (!this.componentName || !this.itemType || !this.items.length || this.nextPage == 0) {
|
||||||
|
promise = this.fetchData(true);
|
||||||
|
} else {
|
||||||
|
promise = Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.then(() => {
|
||||||
|
return this.tagAreaDelegate.getComponent(this.componentName, this.itemType, this.injector).then((component) => {
|
||||||
|
this.areaComponent = component;
|
||||||
|
});
|
||||||
|
}).finally(() => {
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch next page of the tag index area.
|
||||||
|
*
|
||||||
|
* @param {boolean} [refresh=false] Whether to refresh the data or fetch a new page.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
fetchData(refresh: boolean = false): Promise<any> {
|
||||||
|
this.loadMoreError = false;
|
||||||
|
const page = refresh ? 0 : this.nextPage;
|
||||||
|
|
||||||
|
return this.tagProvider.getTagIndexPerArea(this.tagId, this.tagName, this.collectionId, this.areaId, this.fromContextId,
|
||||||
|
this.contextId, this.recursive, page).then((areas) => {
|
||||||
|
const area = areas[0];
|
||||||
|
|
||||||
|
return this.tagAreaDelegate.parseContent(area.component, area.itemtype, area.content).then((items) => {
|
||||||
|
if (!items || !items.length) {
|
||||||
|
// Tag area not supported.
|
||||||
|
return Promise.reject(this.translate.instant('core.tag.errorareanotsupported'));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (page == 0) {
|
||||||
|
this.items = items;
|
||||||
|
} else {
|
||||||
|
this.items.push(...items);
|
||||||
|
}
|
||||||
|
this.componentName = area.component;
|
||||||
|
this.itemType = area.itemtype;
|
||||||
|
this.areaNameKey = this.tagAreaDelegate.getDisplayNameKey(area.component, area.itemtype);
|
||||||
|
this.canLoadMore = !!area.nextpageurl;
|
||||||
|
this.nextPage = page + 1;
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'Error loading tag index');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load more items.
|
||||||
|
*
|
||||||
|
* @param {any} infiniteComplete Infinite scroll complete function.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
loadMore(infiniteComplete: any): Promise<any> {
|
||||||
|
return this.fetchData().finally(() => {
|
||||||
|
infiniteComplete();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh data.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshData(refresher: any): void {
|
||||||
|
this.tagProvider.invalidateTagIndexPerArea(this.tagId, this.tagName, this.collectionId, this.areaId, this.fromContextId,
|
||||||
|
this.contextId, this.recursive).finally(() => {
|
||||||
|
this.fetchData(true).finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar core-back-button>
|
||||||
|
<ion-title>{{ 'core.tag.tag' | translate }}: {{ tagName }}</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<core-split-view>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshData($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<core-loading [hideUntil]="loaded">
|
||||||
|
<ion-list>
|
||||||
|
<ion-item text-wrap *ngIf="hasUnsupportedAreas" class="core-warning-item">
|
||||||
|
<ion-icon item-start name="warning" color="warning"></ion-icon>
|
||||||
|
{{ 'core.tag.warningareasnotsupported' | translate }}
|
||||||
|
</ion-item>
|
||||||
|
<a ion-item text-wrap *ngFor="let area of areas" [title]="area.nameKey | translate" (click)="openArea(area)" [class.core-split-item-selected]="area.id == selectedAreaId">
|
||||||
|
<h2>{{ area.nameKey | translate }}</h2>
|
||||||
|
<ion-badge item-end *ngIf="area.badge">{{ area.badge }}</ion-badge>
|
||||||
|
</a>
|
||||||
|
</ion-list>
|
||||||
|
</core-loading>
|
||||||
|
</ion-content>
|
||||||
|
</core-split-view>
|
|
@ -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 { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { CoreTagIndexPage } from './index';
|
||||||
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
CoreTagIndexPage
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
IonicPageModule.forChild(CoreTagIndexPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CoreTagIndexPageModule {}
|
|
@ -0,0 +1,154 @@
|
||||||
|
// (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, ViewChild } from '@angular/core';
|
||||||
|
import { IonicPage, NavParams } from 'ionic-angular';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
import { CoreTagProvider } from '@core/tag/providers/tag';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays the tag index.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'core-tag-index' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-core-tag-index',
|
||||||
|
templateUrl: 'index.html',
|
||||||
|
})
|
||||||
|
export class CoreTagIndexPage {
|
||||||
|
@ViewChild(CoreSplitViewComponent) splitviewCtrl: CoreSplitViewComponent;
|
||||||
|
|
||||||
|
tagId: number;
|
||||||
|
tagName: string;
|
||||||
|
collectionId: number;
|
||||||
|
areaId: number;
|
||||||
|
fromContextId: number;
|
||||||
|
contextId: number;
|
||||||
|
recursive: boolean;
|
||||||
|
loaded = false;
|
||||||
|
areas: Array<{
|
||||||
|
id: number,
|
||||||
|
componentName: string,
|
||||||
|
itemType: string,
|
||||||
|
nameKey: string,
|
||||||
|
items: any[],
|
||||||
|
canLoadMore: boolean,
|
||||||
|
badge: string
|
||||||
|
}>;
|
||||||
|
selectedAreaId: number;
|
||||||
|
hasUnsupportedAreas = false;
|
||||||
|
|
||||||
|
constructor(navParams: NavParams, private tagProvider: CoreTagProvider, private domUtils: CoreDomUtilsProvider,
|
||||||
|
private tagAreaDelegate: CoreTagAreaDelegate) {
|
||||||
|
this.tagId = navParams.get('tagId') || 0;
|
||||||
|
this.tagName = navParams.get('tagName') || '';
|
||||||
|
this.collectionId = navParams.get('collectionId');
|
||||||
|
this.areaId = navParams.get('areaId') || 0;
|
||||||
|
this.fromContextId = navParams.get('fromContextId') || 0;
|
||||||
|
this.contextId = navParams.get('contextId') || 0;
|
||||||
|
this.recursive = navParams.get('recursive') || true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ionViewDidLoad(): void {
|
||||||
|
this.fetchData().then(() => {
|
||||||
|
if (this.splitviewCtrl.isOn() && this.areas && this.areas.length > 0) {
|
||||||
|
const area = this.areas.find((area) => area.id == this.areaId);
|
||||||
|
this.openArea(area || this.areas[0]);
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch first page of tag index per area.
|
||||||
|
*
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
fetchData(): Promise<any> {
|
||||||
|
return this.tagProvider.getTagIndexPerArea(this.tagId, this.tagName, this.collectionId, this.areaId, this.fromContextId,
|
||||||
|
this.contextId, this.recursive, 0).then((areas) => {
|
||||||
|
this.areas = [];
|
||||||
|
this.hasUnsupportedAreas = false;
|
||||||
|
|
||||||
|
return Promise.all(areas.map((area) => {
|
||||||
|
return this.tagAreaDelegate.parseContent(area.component, area.itemtype, area.content).then((items) => {
|
||||||
|
if (!items || !items.length) {
|
||||||
|
// Tag area not supported, skip.
|
||||||
|
this.hasUnsupportedAreas = true;
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: area.ta,
|
||||||
|
componentName: area.component,
|
||||||
|
itemType: area.itemtype,
|
||||||
|
nameKey: this.tagAreaDelegate.getDisplayNameKey(area.component, area.itemtype),
|
||||||
|
items,
|
||||||
|
canLoadMore: !!area.nextpageurl,
|
||||||
|
badge: items && items.length ? items.length + (area.nextpageurl ? '+' : '') : '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
})).then((areas) => {
|
||||||
|
this.areas = areas.filter((area) => area != null);
|
||||||
|
});
|
||||||
|
}).catch((error) => {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'Error loading tag index');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh data.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshData(refresher: any): void {
|
||||||
|
this.tagProvider.invalidateTagIndexPerArea(this.tagId, this.tagName, this.collectionId, this.areaId, this.fromContextId,
|
||||||
|
this.contextId, this.recursive).finally(() => {
|
||||||
|
this.fetchData().finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Navigate to an index area.
|
||||||
|
*
|
||||||
|
* @param {any} area Area.
|
||||||
|
*/
|
||||||
|
openArea(area: any): void {
|
||||||
|
this.selectedAreaId = area.id;
|
||||||
|
const params = {
|
||||||
|
tagId: this.tagId,
|
||||||
|
tagName: this.tagName,
|
||||||
|
collectionId: this.collectionId,
|
||||||
|
areaId: area.id,
|
||||||
|
fromContextId: this.fromContextId,
|
||||||
|
contextId: this.contextId,
|
||||||
|
recursive: this.recursive,
|
||||||
|
areaNameKey: area.nameKey,
|
||||||
|
componentName: area.component,
|
||||||
|
itemType: area.itemType,
|
||||||
|
items: area.items.slice(),
|
||||||
|
canLoadMore: area.canLoadMore,
|
||||||
|
nextPage: 1
|
||||||
|
};
|
||||||
|
this.splitviewCtrl.push('CoreTagIndexAreaPage', params);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
<ion-header>
|
||||||
|
<ion-navbar core-back-button>
|
||||||
|
<ion-title>{{ 'core.tag.searchtags' | translate }}</ion-title>
|
||||||
|
</ion-navbar>
|
||||||
|
</ion-header>
|
||||||
|
<ion-content>
|
||||||
|
<ion-refresher [enabled]="loaded" (ionRefresh)="refreshData($event)">
|
||||||
|
<ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
|
||||||
|
</ion-refresher>
|
||||||
|
<ion-grid>
|
||||||
|
<ion-row>
|
||||||
|
<ion-col col-12 [attr.col-sm-6]="collections && collections.length > 1 ? '' : null">
|
||||||
|
<core-search-box (onSubmit)="searchTags($event)" (onClear)="searchTags('')" [initialSearch]="query" [disabled]="searching" autocorrect="off" [spellcheck]="false" [autoFocus]="true" [lengthCheck]="0"></core-search-box>
|
||||||
|
</ion-col>
|
||||||
|
<ion-col col-12 col-sm-6 *ngIf="collections && collections.length > 1">
|
||||||
|
<ion-select text-start [(ngModel)]="collectionId" (ngModelChange)="searchTags(query)" [disabled]="searching" interface="popover" class="core-button-select">
|
||||||
|
<ion-option [value]="0">{{ 'core.tag.inalltagcoll' | translate }}</ion-option>
|
||||||
|
<ion-option *ngFor="let collection of collections" [value]="collection.id">{{ collection.name }}</ion-option>
|
||||||
|
</ion-select>
|
||||||
|
</ion-col>
|
||||||
|
</ion-row>
|
||||||
|
</ion-grid>
|
||||||
|
<core-loading [hideUntil]="loaded && !searching">
|
||||||
|
<core-empty-box *ngIf="!cloud || !cloud.tags || !cloud.tags.length" icon="pricetags" [message]="'core.tag.notagsfound' | translate: {$a: query}"></core-empty-box>
|
||||||
|
|
||||||
|
<ng-container *ngIf="cloud && cloud.tags && cloud.tags.length > 0">
|
||||||
|
<div text-center class="core-tag-cloud">
|
||||||
|
<ion-badge *ngFor="let tag of cloud.tags" (click)="openTag(tag)" text-wrap>
|
||||||
|
<span [class]="'size' + tag.size" >{{ tag.name }}</span>
|
||||||
|
</ion-badge>
|
||||||
|
</div>
|
||||||
|
<p *ngIf="cloud.tags.length < cloud.totalcount" text-center>
|
||||||
|
{{ 'core.tag.showingfirsttags' | translate: {$a: cloud.tags.length} }}
|
||||||
|
</p>
|
||||||
|
</ng-container>
|
||||||
|
</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 { TranslateModule } from '@ngx-translate/core';
|
||||||
|
import { CoreTagSearchPage } from './search';
|
||||||
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
CoreTagSearchPage
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
CoreComponentsModule,
|
||||||
|
CoreDirectivesModule,
|
||||||
|
IonicPageModule.forChild(CoreTagSearchPage),
|
||||||
|
TranslateModule.forChild()
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class CoreTagSerchPageModule {}
|
|
@ -0,0 +1,95 @@
|
||||||
|
ion-app.app-root page-core-tag-search {
|
||||||
|
core-search-box ion-card {
|
||||||
|
width: 100% !important;
|
||||||
|
margin: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.core-tag-cloud ion-badge {
|
||||||
|
margin: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
.size20 {
|
||||||
|
font-size: 3.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size19 {
|
||||||
|
font-size: 3.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size18 {
|
||||||
|
font-size: 3.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size17 {
|
||||||
|
font-size: 3.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size16 {
|
||||||
|
font-size: 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size15 {
|
||||||
|
font-size: 2.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size14 {
|
||||||
|
font-size: 2.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size13 {
|
||||||
|
font-size: 2.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size12 {
|
||||||
|
font-size: 2.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size11 {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size10 {
|
||||||
|
font-size: 2.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size9 {
|
||||||
|
font-size: 2.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size8 {
|
||||||
|
font-size: 2.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size7 {
|
||||||
|
font-size: 2.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size6 {
|
||||||
|
font-size: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size5 {
|
||||||
|
font-size: 1.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size4 {
|
||||||
|
font-size: 1.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size3 {
|
||||||
|
font-size: 1.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size2 {
|
||||||
|
font-size: 1.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size1 {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.size0 {
|
||||||
|
font-size: 1.4rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
// (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 { TranslateService } from '@ngx-translate/core';
|
||||||
|
import { CoreAppProvider } from '@providers/app';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||||
|
import { CoreTagProvider, CoreTagCloud, CoreTagCollection, CoreTagCloudTag } from '@core/tag/providers/tag';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Page that displays most used tags and allows searching.
|
||||||
|
*/
|
||||||
|
@IonicPage({ segment: 'core-tag-search' })
|
||||||
|
@Component({
|
||||||
|
selector: 'page-core-tag-search',
|
||||||
|
templateUrl: 'search.html',
|
||||||
|
})
|
||||||
|
export class CoreTagSearchPage {
|
||||||
|
collectionId: number;
|
||||||
|
query: string;
|
||||||
|
collections: CoreTagCollection[] = [];
|
||||||
|
cloud: CoreTagCloud;
|
||||||
|
loaded = false;
|
||||||
|
searching = false;
|
||||||
|
|
||||||
|
constructor(private navCtrl: NavController, navParams: NavParams, private appProvider: CoreAppProvider,
|
||||||
|
private translate: TranslateService, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider,
|
||||||
|
private textUtils: CoreTextUtilsProvider, private contentLinksHelper: CoreContentLinksHelperProvider,
|
||||||
|
private tagProvider: CoreTagProvider) {
|
||||||
|
this.collectionId = navParams.get('collectionId') || 0;
|
||||||
|
this.query = navParams.get('query') || '';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* View loaded.
|
||||||
|
*/
|
||||||
|
ionViewDidLoad(): void {
|
||||||
|
this.fetchData().finally(() => {
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchData(): Promise<any> {
|
||||||
|
return Promise.all([
|
||||||
|
this.fetchCollections(),
|
||||||
|
this.fetchTags()
|
||||||
|
]).catch((error) => {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'Error loading tags.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch tag collections.
|
||||||
|
*
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
fetchCollections(): Promise<any> {
|
||||||
|
return this.tagProvider.getTagCollections().then((collections) => {
|
||||||
|
collections.forEach((collection) => {
|
||||||
|
if (!collection.name && collection.isdefault) {
|
||||||
|
collection.name = this.translate.instant('core.tag.defautltagcoll');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.collections = collections;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch tags.
|
||||||
|
*
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
fetchTags(): Promise<any> {
|
||||||
|
return this.tagProvider.getTagCloud(this.collectionId, undefined, undefined, this.query).then((cloud) => {
|
||||||
|
this.cloud = cloud;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Go to tag index page.
|
||||||
|
*/
|
||||||
|
openTag(tag: CoreTagCloudTag): void {
|
||||||
|
const url = this.textUtils.decodeURI(tag.viewurl);
|
||||||
|
this.contentLinksHelper.handleLink(url, undefined, this.navCtrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Refresh data.
|
||||||
|
*
|
||||||
|
* @param {any} refresher Refresher.
|
||||||
|
*/
|
||||||
|
refreshData(refresher: any): void {
|
||||||
|
this.utils.allPromises([
|
||||||
|
this.tagProvider.invalidateTagCollections(),
|
||||||
|
this.tagProvider.invalidateTagCloud(this.collectionId, undefined, undefined, this.query),
|
||||||
|
]).finally(() => {
|
||||||
|
return this.fetchData().finally(() => {
|
||||||
|
refresher.complete();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Search tags.
|
||||||
|
*
|
||||||
|
* @param {string} query Search query.
|
||||||
|
* @return {Promise<any>} Resolved when done.
|
||||||
|
*/
|
||||||
|
searchTags(query: string): Promise<any> {
|
||||||
|
this.searching = true;
|
||||||
|
this.query = query;
|
||||||
|
this.appProvider.closeKeyboard();
|
||||||
|
|
||||||
|
return this.fetchTags().catch((error) => {
|
||||||
|
this.domUtils.showErrorModalDefault(error, 'Error loading tags.');
|
||||||
|
}).finally(() => {
|
||||||
|
this.searching = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreEventsProvider } from '@providers/events';
|
||||||
|
import { CoreLoggerProvider } from '@providers/logger';
|
||||||
|
import { CoreSitesProvider } from '@providers/sites';
|
||||||
|
import { CoreDelegate, CoreDelegateHandler } from '@classes/delegate';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Interface that all tag area handlers must implement.
|
||||||
|
*/
|
||||||
|
export interface CoreTagAreaHandler extends CoreDelegateHandler {
|
||||||
|
/**
|
||||||
|
* Component and item type separated by a slash. E.g. 'core/course_modules'.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
type: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Delegate to register tag area handlers.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreTagAreaDelegate extends CoreDelegate {
|
||||||
|
|
||||||
|
protected handlerNameProperty = 'type';
|
||||||
|
|
||||||
|
constructor(logger: CoreLoggerProvider, sitesProvider: CoreSitesProvider, eventsProvider: CoreEventsProvider) {
|
||||||
|
super('CoreTagAreaDelegate', logger, sitesProvider, eventsProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the display name string for this area.
|
||||||
|
*
|
||||||
|
* @param {string} component Component name.
|
||||||
|
* @param {string} itemType Item type.
|
||||||
|
* @return {string} String key.
|
||||||
|
*/
|
||||||
|
getDisplayNameKey(component: string, itemType: string): string {
|
||||||
|
return (component == 'core' ? 'core.tag' : 'addon.' + component) + '.tagarea_' + itemType;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} component Component name.
|
||||||
|
* @param {string} itemType Item type.
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {Promise<any[]>} Promise resolved with the area items, or undefined if not found.
|
||||||
|
*/
|
||||||
|
parseContent(component: string, itemType: string, content: string): Promise<any[]> {
|
||||||
|
const type = component + '/' + itemType;
|
||||||
|
|
||||||
|
return Promise.resolve(this.executeFunctionOnEnabled(type, 'parseContent', [content]));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display an area item.
|
||||||
|
*
|
||||||
|
* @param {string} component Component name.
|
||||||
|
* @param {string} itemType Item type.
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(component: string, itemType: string, injector: Injector): Promise<any> {
|
||||||
|
const type = component + '/' + itemType;
|
||||||
|
|
||||||
|
return Promise.resolve(this.executeFunctionOnEnabled(type, 'getComponent', [injector]));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// (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 { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service with helper functions for tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreTagHelperProvider {
|
||||||
|
|
||||||
|
constructor(protected domUtils: CoreDomUtilsProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of the "core_tag/tagfeed" web template and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]} Area items.
|
||||||
|
*/
|
||||||
|
parseFeedContent(content: string): any[] {
|
||||||
|
const items = [];
|
||||||
|
const element = this.domUtils.convertToElement(content);
|
||||||
|
|
||||||
|
Array.from(element.querySelectorAll('ul.tag_feed > li.media')).forEach((itemElement) => {
|
||||||
|
const item: any = { details: [] };
|
||||||
|
|
||||||
|
Array.from(itemElement.querySelectorAll('div.media-body > div')).forEach((div: HTMLElement) => {
|
||||||
|
if (div.classList.contains('media-heading')) {
|
||||||
|
item.heading = div.innerText.trim();
|
||||||
|
const link = div.querySelector('a');
|
||||||
|
if (link) {
|
||||||
|
item.url = link.getAttribute('href');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Separate details by lines.
|
||||||
|
const lines = [''];
|
||||||
|
Array.from(div.childNodes).forEach((childNode: Node) => {
|
||||||
|
if (childNode.nodeType == Node.TEXT_NODE) {
|
||||||
|
lines[lines.length - 1] += childNode.textContent;
|
||||||
|
} else if (childNode.nodeType == Node.ELEMENT_NODE) {
|
||||||
|
const childElement = childNode as HTMLElement;
|
||||||
|
if (childElement.tagName == 'BR') {
|
||||||
|
lines.push('');
|
||||||
|
} else {
|
||||||
|
lines[lines.length - 1] += childElement.innerText;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
item.details.push(...lines.map((line) => line.trim()).filter((line) => line != ''));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const image = itemElement.querySelector('div.itemimage img');
|
||||||
|
if (image) {
|
||||||
|
if (image.classList.contains('userpicture')) {
|
||||||
|
item.avatarUrl = image.getAttribute('src');
|
||||||
|
} else {
|
||||||
|
item.iconUrl = image.getAttribute('src');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.heading && item.url) {
|
||||||
|
items.push(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
// (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 '@core/contentlinks/classes/base-handler';
|
||||||
|
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
|
||||||
|
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||||
|
import { CoreTagProvider } from './tag';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to tag index.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreTagIndexLinkHandler extends CoreContentLinksHandlerBase {
|
||||||
|
name = 'CoreTagIndexLinkHandler';
|
||||||
|
pattern = /\/tag\/index\.php/;
|
||||||
|
|
||||||
|
constructor(private tagProvider: CoreTagProvider, private linkHelper: CoreContentLinksHelperProvider) {
|
||||||
|
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.
|
||||||
|
* @param {any} [data] Extra data to handle the URL.
|
||||||
|
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||||
|
*/
|
||||||
|
getActions(siteIds: string[], url: string, params: any, courseId?: number, data?: any):
|
||||||
|
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
|
||||||
|
return [{
|
||||||
|
action: (siteId, navCtrl?): void => {
|
||||||
|
const pageParams = {
|
||||||
|
tagId: parseInt(params.id, 10) || 0,
|
||||||
|
tagName: params.tag || '',
|
||||||
|
collectionId: parseInt(params.tc, 10) || 0,
|
||||||
|
areaId: parseInt(params.ta, 10) || 0,
|
||||||
|
fromContextId: parseInt(params.from, 10) || 0,
|
||||||
|
contextId: parseInt(params.ctx, 10) || 0,
|
||||||
|
recursive: parseInt(params.rec, 10) || 1
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!pageParams.tagId && (!pageParams.tagName || !pageParams.collectionId)) {
|
||||||
|
this.linkHelper.goInSite(navCtrl, 'CoreTagSearchPage', {}, siteId);
|
||||||
|
} else if (pageParams.areaId) {
|
||||||
|
this.linkHelper.goInSite(navCtrl, 'CoreTagIndexAreaPage', pageParams, siteId);
|
||||||
|
} else {
|
||||||
|
this.linkHelper.goInSite(navCtrl, 'CoreTagIndexPage', 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> {
|
||||||
|
return this.tagProvider.areTagsAvailable(siteId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
// (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 { CoreTagProvider } from './tag';
|
||||||
|
import { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@core/mainmenu/providers/delegate';
|
||||||
|
import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to inject an option into main menu.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreTagMainMenuHandler implements CoreMainMenuHandler {
|
||||||
|
name = 'CoreTag';
|
||||||
|
priority = 300;
|
||||||
|
|
||||||
|
constructor(private tagProvider: CoreTagProvider, private utils: CoreUtilsProvider) { }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the handler is enabled on a site level.
|
||||||
|
*
|
||||||
|
* @return {boolean | Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return this.tagProvider.areTagsAvailable().then((available) => {
|
||||||
|
if (!available) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The only way to check whether tags are enabled on web is to perform a WS call.
|
||||||
|
return this.utils.promiseWorks(this.tagProvider.getTagCollections());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the data needed to render the handler.
|
||||||
|
*
|
||||||
|
* @return {CoreMainMenuHandlerData} Data needed to render the handler.
|
||||||
|
*/
|
||||||
|
getDisplayData(): CoreMainMenuHandlerData {
|
||||||
|
return {
|
||||||
|
icon: 'pricetags',
|
||||||
|
title: 'core.tag.tags',
|
||||||
|
page: 'CoreTagSearchPage',
|
||||||
|
class: 'core-tag-search-handler'
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
// (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 '@core/contentlinks/classes/base-handler';
|
||||||
|
import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
|
||||||
|
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||||
|
import { CoreTagProvider } from './tag';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to treat links to tag search.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreTagSearchLinkHandler extends CoreContentLinksHandlerBase {
|
||||||
|
name = 'CoreTagSearchLinkHandler';
|
||||||
|
pattern = /\/tag\/search\.php/;
|
||||||
|
|
||||||
|
constructor(private tagProvider: CoreTagProvider, private linkHelper: CoreContentLinksHelperProvider) {
|
||||||
|
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.
|
||||||
|
* @param {any} [data] Extra data to handle the URL.
|
||||||
|
* @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
|
||||||
|
*/
|
||||||
|
getActions(siteIds: string[], url: string, params: any, courseId?: number, data?: any):
|
||||||
|
CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
|
||||||
|
return [{
|
||||||
|
action: (siteId, navCtrl?): void => {
|
||||||
|
const pageParams = {
|
||||||
|
collectionId: parseInt(params.tc, 10) || 0,
|
||||||
|
query: params.query || '',
|
||||||
|
};
|
||||||
|
|
||||||
|
this.linkHelper.goInSite(navCtrl, 'CoreTagSearchPage', 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> {
|
||||||
|
return this.tagProvider.areTagsAvailable(siteId);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,345 @@
|
||||||
|
// (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 { CoreSite, CoreSiteWSPreSets } from '@classes/site';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure of a tag cloud returned by WS.
|
||||||
|
*/
|
||||||
|
export interface CoreTagCloud {
|
||||||
|
tags: CoreTagCloudTag[];
|
||||||
|
tagscount: number;
|
||||||
|
totalcount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure of a tag cloud tag returned by WS.
|
||||||
|
*/
|
||||||
|
export interface CoreTagCloudTag {
|
||||||
|
name: string;
|
||||||
|
viewurl: string;
|
||||||
|
flag: boolean;
|
||||||
|
isstandard: boolean;
|
||||||
|
count: number;
|
||||||
|
size: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure of a tag collection returned by WS.
|
||||||
|
*/
|
||||||
|
export interface CoreTagCollection {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
isdefault: boolean;
|
||||||
|
component: string;
|
||||||
|
sortoder: number;
|
||||||
|
searchable: boolean;
|
||||||
|
customurl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure of a tag index returned by WS.
|
||||||
|
*/
|
||||||
|
export interface CoreTagIndex {
|
||||||
|
tagid: number;
|
||||||
|
ta: number;
|
||||||
|
component: string;
|
||||||
|
itemtype: string;
|
||||||
|
nextpageurl: string;
|
||||||
|
prevpageurl: string;
|
||||||
|
exclusiveurl: string;
|
||||||
|
exclusivetext: string;
|
||||||
|
title: string;
|
||||||
|
content: string;
|
||||||
|
hascontent: number;
|
||||||
|
anchor: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Structure of a tag item returned by WS.
|
||||||
|
*/
|
||||||
|
export interface CoreTagItem {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
rawname: string;
|
||||||
|
isstandard: boolean;
|
||||||
|
tagcollid: number;
|
||||||
|
taginstanceid: number;
|
||||||
|
taginstancecontextid: number;
|
||||||
|
itemid: number;
|
||||||
|
ordering: number;
|
||||||
|
flag: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Service to handle tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreTagProvider {
|
||||||
|
|
||||||
|
static SEARCH_LIMIT = 150;
|
||||||
|
|
||||||
|
protected ROOT_CACHE_KEY = 'CoreTag:';
|
||||||
|
|
||||||
|
constructor(private sitesProvider: CoreSitesProvider, private translate: TranslateService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether tags are available in a certain site.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site Id. If not defined, use current site.
|
||||||
|
* @return {Promise<boolean>} Promise resolved with true if available, resolved with false otherwise.
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
areTagsAvailable(siteId?: string): Promise<boolean> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
return this.areTagsAvailableInSite(site);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check whether tags are available in a certain site.
|
||||||
|
*
|
||||||
|
* @param {CoreSite} [site] Site. If not defined, use current site.
|
||||||
|
* @return {boolean} True if available.
|
||||||
|
*/
|
||||||
|
areTagsAvailableInSite(site?: CoreSite): boolean {
|
||||||
|
site = site || this.sitesProvider.getCurrentSite();
|
||||||
|
|
||||||
|
return site.wsAvailable('core_tag_get_tagindex_per_area') &&
|
||||||
|
site.wsAvailable('core_tag_get_tag_cloud') &&
|
||||||
|
site.wsAvailable('core_tag_get_tag_collections') &&
|
||||||
|
!site.isFeatureDisabled('NoDelegate_CoreTag');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the tag cloud.
|
||||||
|
*
|
||||||
|
* @param {number} [collectionId=0] Tag collection ID.
|
||||||
|
* @param {boolean} [isStandard=false] Whether to return only standard tags.
|
||||||
|
* @param {string} [sort='name'] Sort order for display (id, name, rawname, count, flag, isstandard, tagcollid).
|
||||||
|
* @param {string} [search=''] Search string.
|
||||||
|
* @param {number} [fromContextId=0] Context ID where this tag cloud is displayed.
|
||||||
|
* @param {number} [contextId=0] Only retrieve tag instances in this context.
|
||||||
|
* @param {boolean} [recursive=true] Retrieve tag instances in the context and its children.
|
||||||
|
* @param {number} [limit] Maximum number of tags to retrieve. Defaults to SEARCH_LIMIT.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<CoreTagCloud>} Promise resolved with the tag cloud.
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
getTagCloud(collectionId: number = 0, isStandard: boolean = false, sort: string = 'name', search: string = '',
|
||||||
|
fromContextId: number = 0, contextId: number = 0, recursive: boolean = true, limit?: number, siteId?: string):
|
||||||
|
Promise<CoreTagCloud> {
|
||||||
|
limit = limit || CoreTagProvider.SEARCH_LIMIT;
|
||||||
|
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const params = {
|
||||||
|
tagcollid: collectionId,
|
||||||
|
isstandard: isStandard,
|
||||||
|
limit: limit,
|
||||||
|
sort: sort,
|
||||||
|
search: search,
|
||||||
|
fromctx: fromContextId,
|
||||||
|
ctx: contextId,
|
||||||
|
rec: recursive
|
||||||
|
};
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
|
||||||
|
cacheKey: this.getTagCloudKey(collectionId, isStandard, sort, search, fromContextId, contextId, recursive),
|
||||||
|
getFromCache: search != '' // Try to get updated data when searching.
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_tag_get_tag_cloud', params, preSets);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the tag collections.
|
||||||
|
*
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<CoreTagCollection[]>} Promise resolved with the tag collections.
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
getTagCollections(siteId?: string): Promise<CoreTagCollection[]> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
updateFrequency: CoreSite.FREQUENCY_RARELY,
|
||||||
|
cacheKey: this.getTagCollectionsKey()
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_tag_get_tag_collections', null, preSets).then((response) => {
|
||||||
|
if (!response || !response.collections) {
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.collections;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetch the tag index.
|
||||||
|
*
|
||||||
|
* @param {number} [id=0] Tag ID.
|
||||||
|
* @param {string} [name=''] Tag name.
|
||||||
|
* @param {number} [collectionId=0] Tag collection ID.
|
||||||
|
* @param {number} [areaId=0] Tag area ID.
|
||||||
|
* @param {number} [fromContextId=0] Context ID where the link was displayed.
|
||||||
|
* @param {number} [contextId=0] Context ID where to search for items.
|
||||||
|
* @param {boolean} [recursive=true] Search in the context and its children.
|
||||||
|
* @param {number} [page=0] Page number.
|
||||||
|
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||||
|
* @return {Promise<CoreTagIndex[]>} Promise resolved with the tag index per area.
|
||||||
|
* @since 3.7
|
||||||
|
*/
|
||||||
|
getTagIndexPerArea(id: number, name: string = '', collectionId: number = 0, areaId: number = 0, fromContextId: number = 0,
|
||||||
|
contextId: number = 0, recursive: boolean = true, page: number = 0, siteId?: string): Promise<CoreTagIndex[]> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const params = {
|
||||||
|
tagindex: {
|
||||||
|
id: id,
|
||||||
|
tag: name,
|
||||||
|
tc: collectionId,
|
||||||
|
ta: areaId,
|
||||||
|
excl: true,
|
||||||
|
from: fromContextId,
|
||||||
|
ctx: contextId,
|
||||||
|
rec: recursive,
|
||||||
|
page: page
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const preSets: CoreSiteWSPreSets = {
|
||||||
|
updateFrequency: CoreSite.FREQUENCY_OFTEN,
|
||||||
|
cacheKey: this.getTagIndexPerAreaKey(id, name, collectionId, areaId, fromContextId, contextId, recursive)
|
||||||
|
};
|
||||||
|
|
||||||
|
return site.read('core_tag_get_tagindex_per_area', params, preSets).catch((error) => {
|
||||||
|
// Workaround for WS not passing parameter to error string.
|
||||||
|
if (error && error.errorcode == 'notagsfound') {
|
||||||
|
error.message = this.translate.instant('core.tag.notagsfound', {$a: name || id || ''});
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.reject(error);
|
||||||
|
}).then((response) => {
|
||||||
|
if (!response || !response.length) {
|
||||||
|
return Promise.reject(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate tag cloud.
|
||||||
|
*
|
||||||
|
* @param {number} [collectionId=0] Tag collection ID.
|
||||||
|
* @param {boolean} [isStandard=false] Whether to return only standard tags.
|
||||||
|
* @param {string} [sort='name'] Sort order for display (id, name, rawname, count, flag, isstandard, tagcollid).
|
||||||
|
* @param {string} [search=''] Search string.
|
||||||
|
* @param {number} [fromContextId=0] Context ID where this tag cloud is displayed.
|
||||||
|
* @param {number} [contextId=0] Only retrieve tag instances in this context.
|
||||||
|
* @param {boolean} [recursive=true] Retrieve tag instances in the context and its children.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateTagCloud(collectionId: number = 0, isStandard: boolean = false, sort: string = 'name', search: string = '',
|
||||||
|
fromContextId: number = 0, contextId: number = 0, recursive: boolean = true, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const key = this.getTagCloudKey(collectionId, isStandard, sort, search, fromContextId, contextId, recursive);
|
||||||
|
|
||||||
|
return site.invalidateWsCacheForKey(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate tag collections.
|
||||||
|
*
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateTagCollections(siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const key = this.getTagCollectionsKey();
|
||||||
|
|
||||||
|
return site.invalidateWsCacheForKey(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate tag index.
|
||||||
|
*
|
||||||
|
* @param {number} [id=0] Tag ID.
|
||||||
|
* @param {string} [name=''] Tag name.
|
||||||
|
* @param {number} [collectionId=0] Tag collection ID.
|
||||||
|
* @param {number} [areaId=0] Tag area ID.
|
||||||
|
* @param {number} [fromContextId=0] Context ID where the link was displayed.
|
||||||
|
* @param {number} [contextId=0] Context ID where to search for items.
|
||||||
|
* @param {boolean} [recursive=true] Search in the context and its children.
|
||||||
|
* @return {Promise<any>} Promise resolved when the data is invalidated.
|
||||||
|
*/
|
||||||
|
invalidateTagIndexPerArea(id: number, name: string = '', collectionId: number = 0, areaId: number = 0,
|
||||||
|
fromContextId: number = 0, contextId: number = 0, recursive: boolean = true, siteId?: string): Promise<any> {
|
||||||
|
return this.sitesProvider.getSite(siteId).then((site) => {
|
||||||
|
const key = this.getTagIndexPerAreaKey(id, name, collectionId, areaId, fromContextId, contextId, recursive);
|
||||||
|
|
||||||
|
return site.invalidateWsCacheForKey(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for tag cloud.
|
||||||
|
*
|
||||||
|
* @param {number} collectionId Tag collection ID.
|
||||||
|
* @param {boolean} isStandard Whether to return only standard tags.
|
||||||
|
* @param {string} sort Sort order for display (id, name, rawname, count, flag, isstandard, tagcollid).
|
||||||
|
* @param {string} search Search string.
|
||||||
|
* @param {number} fromContextId Context ID where this tag cloud is displayed.
|
||||||
|
* @param {number} contextId Only retrieve tag instances in this context.
|
||||||
|
* @param {boolean} recursive Retrieve tag instances in the context and it's children.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getTagCloudKey(collectionId: number, isStandard: boolean, sort: string, search: string, fromContextId: number,
|
||||||
|
contextId: number, recursive: boolean): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'cloud:' + collectionId + ':' + (isStandard ? 1 : 0) + ':' + sort + ':' + search + ':' +
|
||||||
|
fromContextId + ':' + contextId + ':' + (recursive ? 1 : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for tag collections.
|
||||||
|
*
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getTagCollectionsKey(): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'collections';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get cache key for tag index.
|
||||||
|
*
|
||||||
|
* @param {number} id Tag ID.
|
||||||
|
* @param {string} name Tag name.
|
||||||
|
* @param {number} collectionId Tag collection ID.
|
||||||
|
* @param {number} areaId Tag area ID.
|
||||||
|
* @param {number} fromContextId Context ID where the link was displayed.
|
||||||
|
* @param {number} contextId Context ID where to search for items.
|
||||||
|
* @param {boolean} [recursive=true] Search in the context and its children.
|
||||||
|
* @return {string} Cache key.
|
||||||
|
*/
|
||||||
|
protected getTagIndexPerAreaKey(id: number, name: string, collectionId: number, areaId: number, fromContextId: number,
|
||||||
|
contextId: number, recursive: boolean): string {
|
||||||
|
return this.ROOT_CACHE_KEY + 'index:' + id + ':' + name + ':' + collectionId + ':' + areaId + ':' + fromContextId + ':'
|
||||||
|
+ contextId + ':' + (recursive ? 1 : 0);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
// (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 { CoreMainMenuDelegate } from '@core/mainmenu/providers/delegate';
|
||||||
|
import { CoreContentLinksDelegate } from '@core/contentlinks/providers/delegate';
|
||||||
|
import { CoreTagProvider } from './providers/tag';
|
||||||
|
import { CoreTagHelperProvider } from './providers/helper';
|
||||||
|
import { CoreTagAreaDelegate } from './providers/area-delegate';
|
||||||
|
import { CoreTagMainMenuHandler } from './providers/mainmenu-handler';
|
||||||
|
import { CoreTagIndexLinkHandler } from './providers/index-link-handler';
|
||||||
|
import { CoreTagSearchLinkHandler } from './providers/search-link-handler';
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [
|
||||||
|
],
|
||||||
|
imports: [
|
||||||
|
],
|
||||||
|
providers: [
|
||||||
|
CoreTagProvider,
|
||||||
|
CoreTagHelperProvider,
|
||||||
|
CoreTagAreaDelegate,
|
||||||
|
CoreTagMainMenuHandler,
|
||||||
|
CoreTagIndexLinkHandler,
|
||||||
|
CoreTagSearchLinkHandler
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class CoreTagModule {
|
||||||
|
|
||||||
|
constructor(mainMenuDelegate: CoreMainMenuDelegate, mainMenuHandler: CoreTagMainMenuHandler,
|
||||||
|
contentLinksDelegate: CoreContentLinksDelegate, indexLinkHandler: CoreTagIndexLinkHandler,
|
||||||
|
searchLinkHandler: CoreTagSearchLinkHandler) {
|
||||||
|
mainMenuDelegate.registerHandler(mainMenuHandler);
|
||||||
|
contentLinksDelegate.registerHandler(indexLinkHandler);
|
||||||
|
contentLinksDelegate.registerHandler(searchLinkHandler);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,6 +18,7 @@ import { IonicModule } from 'ionic-angular';
|
||||||
import { TranslateModule } from '@ngx-translate/core';
|
import { TranslateModule } from '@ngx-translate/core';
|
||||||
import { CoreUserParticipantsComponent } from './participants/participants';
|
import { CoreUserParticipantsComponent } from './participants/participants';
|
||||||
import { CoreUserProfileFieldComponent } from './user-profile-field/user-profile-field';
|
import { CoreUserProfileFieldComponent } from './user-profile-field/user-profile-field';
|
||||||
|
import { CoreUserTagAreaComponent } from './tag-area/tag-area';
|
||||||
import { CoreComponentsModule } from '@components/components.module';
|
import { CoreComponentsModule } from '@components/components.module';
|
||||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||||
import { CorePipesModule } from '@pipes/pipes.module';
|
import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
|
@ -25,7 +26,8 @@ import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
CoreUserParticipantsComponent,
|
CoreUserParticipantsComponent,
|
||||||
CoreUserProfileFieldComponent
|
CoreUserProfileFieldComponent,
|
||||||
|
CoreUserTagAreaComponent
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
CommonModule,
|
CommonModule,
|
||||||
|
@ -39,10 +41,12 @@ import { CorePipesModule } from '@pipes/pipes.module';
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
CoreUserParticipantsComponent,
|
CoreUserParticipantsComponent,
|
||||||
CoreUserProfileFieldComponent
|
CoreUserProfileFieldComponent,
|
||||||
|
CoreUserTagAreaComponent
|
||||||
],
|
],
|
||||||
entryComponents: [
|
entryComponents: [
|
||||||
CoreUserParticipantsComponent
|
CoreUserParticipantsComponent,
|
||||||
|
CoreUserTagAreaComponent
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreUserComponentsModule {}
|
export class CoreUserComponentsModule {}
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
<a ion-item text-wrap *ngFor="let item of items" core-user-link [userId]="item.id">
|
||||||
|
<ion-avatar core-user-avatar [user]="item" item-start></ion-avatar>
|
||||||
|
<h2>{{ item.fullname }}</h2>
|
||||||
|
</a>
|
|
@ -0,0 +1,26 @@
|
||||||
|
// (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';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Component to render the user tag area.
|
||||||
|
*/
|
||||||
|
@Component({
|
||||||
|
selector: 'core-user-tag-area',
|
||||||
|
templateUrl: 'core-user-tag-area.html'
|
||||||
|
})
|
||||||
|
export class CoreUserTagAreaComponent {
|
||||||
|
@Input() items: any[]; // Area items to render.
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// (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, Injector } from '@angular/core';
|
||||||
|
import { CoreDomUtilsProvider } from '@providers/utils/dom';
|
||||||
|
import { CoreTagAreaHandler } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreUserTagAreaComponent } from '../components/tag-area/tag-area';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler to support tags.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class CoreUserTagAreaHandler implements CoreTagAreaHandler {
|
||||||
|
name = 'CoreUserTagAreaHandler';
|
||||||
|
type = 'core/user';
|
||||||
|
|
||||||
|
constructor(private domUtils: CoreDomUtilsProvider) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Whether or not the handler is enabled on a site level.
|
||||||
|
* @return {boolean|Promise<boolean>} Whether or not the handler is enabled on a site level.
|
||||||
|
*/
|
||||||
|
isEnabled(): boolean | Promise<boolean> {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the rendered content of a tag index and returns the items.
|
||||||
|
*
|
||||||
|
* @param {string} content Rendered content.
|
||||||
|
* @return {any[]|Promise<any[]>} Area items (or promise resolved with the items).
|
||||||
|
*/
|
||||||
|
parseContent(content: string): any[] | Promise<any[]> {
|
||||||
|
const items = [];
|
||||||
|
const element = this.domUtils.convertToElement(content);
|
||||||
|
|
||||||
|
Array.from(element.querySelectorAll('div.user-box')).forEach((userbox: HTMLElement) => {
|
||||||
|
const item: any = {};
|
||||||
|
|
||||||
|
const avatarLink = userbox.querySelector('a:first-child');
|
||||||
|
if (!avatarLink) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const profileUrl = avatarLink.getAttribute('href') || '';
|
||||||
|
const match = profileUrl.match(/.*\/user\/(?:profile|view)\.php\?id=(\d+)/);
|
||||||
|
if (!match) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
item.id = parseInt(match[1], 10);
|
||||||
|
const avatarImg = avatarLink.querySelector('img.userpicture');
|
||||||
|
item.profileimageurl = avatarImg ? avatarImg.getAttribute('src') : '';
|
||||||
|
item.fullname = userbox.innerText;
|
||||||
|
|
||||||
|
items.push(item);
|
||||||
|
});
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the component to use to display items.
|
||||||
|
*
|
||||||
|
* @param {Injector} injector Injector.
|
||||||
|
* @return {any|Promise<any>} The component (or promise resolved with component) to use, undefined if not found.
|
||||||
|
*/
|
||||||
|
getComponent(injector: Injector): any | Promise<any> {
|
||||||
|
return CoreUserTagAreaComponent;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,6 +30,8 @@ import { CoreCronDelegate } from '@providers/cron';
|
||||||
import { CoreUserOfflineProvider } from './providers/offline';
|
import { CoreUserOfflineProvider } from './providers/offline';
|
||||||
import { CoreUserSyncProvider } from './providers/sync';
|
import { CoreUserSyncProvider } from './providers/sync';
|
||||||
import { CoreUserSyncCronHandler } from './providers/sync-cron-handler';
|
import { CoreUserSyncCronHandler } from './providers/sync-cron-handler';
|
||||||
|
import { CoreTagAreaDelegate } from '@core/tag/providers/area-delegate';
|
||||||
|
import { CoreUserTagAreaHandler } from './providers/tag-area-handler';
|
||||||
|
|
||||||
// List of providers (without handlers).
|
// List of providers (without handlers).
|
||||||
export const CORE_USER_PROVIDERS: any[] = [
|
export const CORE_USER_PROVIDERS: any[] = [
|
||||||
|
@ -59,6 +61,7 @@ export const CORE_USER_PROVIDERS: any[] = [
|
||||||
CoreUserParticipantsCourseOptionHandler,
|
CoreUserParticipantsCourseOptionHandler,
|
||||||
CoreUserParticipantsLinkHandler,
|
CoreUserParticipantsLinkHandler,
|
||||||
CoreUserSyncCronHandler,
|
CoreUserSyncCronHandler,
|
||||||
|
CoreUserTagAreaHandler
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
export class CoreUserModule {
|
export class CoreUserModule {
|
||||||
|
@ -67,13 +70,14 @@ export class CoreUserModule {
|
||||||
contentLinksDelegate: CoreContentLinksDelegate, userLinkHandler: CoreUserProfileLinkHandler,
|
contentLinksDelegate: CoreContentLinksDelegate, userLinkHandler: CoreUserProfileLinkHandler,
|
||||||
courseOptionHandler: CoreUserParticipantsCourseOptionHandler, linkHandler: CoreUserParticipantsLinkHandler,
|
courseOptionHandler: CoreUserParticipantsCourseOptionHandler, linkHandler: CoreUserParticipantsLinkHandler,
|
||||||
courseOptionsDelegate: CoreCourseOptionsDelegate, cronDelegate: CoreCronDelegate,
|
courseOptionsDelegate: CoreCourseOptionsDelegate, cronDelegate: CoreCronDelegate,
|
||||||
syncHandler: CoreUserSyncCronHandler) {
|
syncHandler: CoreUserSyncCronHandler, tagAreaDelegate: CoreTagAreaDelegate, tagAreaHandler: CoreUserTagAreaHandler) {
|
||||||
|
|
||||||
userDelegate.registerHandler(userProfileMailHandler);
|
userDelegate.registerHandler(userProfileMailHandler);
|
||||||
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
courseOptionsDelegate.registerHandler(courseOptionHandler);
|
||||||
contentLinksDelegate.registerHandler(userLinkHandler);
|
contentLinksDelegate.registerHandler(userLinkHandler);
|
||||||
contentLinksDelegate.registerHandler(linkHandler);
|
contentLinksDelegate.registerHandler(linkHandler);
|
||||||
cronDelegate.register(syncHandler);
|
cronDelegate.register(syncHandler);
|
||||||
|
tagAreaDelegate.registerHandler(tagAreaHandler);
|
||||||
|
|
||||||
eventsProvider.on(CoreEventsProvider.USER_DELETED, (data) => {
|
eventsProvider.on(CoreEventsProvider.USER_DELETED, (data) => {
|
||||||
// Search for userid in params.
|
// Search for userid in params.
|
||||||
|
|
|
@ -394,7 +394,7 @@ export class CoreFormatTextDirective implements OnChanges {
|
||||||
anchors.forEach((anchor) => {
|
anchors.forEach((anchor) => {
|
||||||
// Angular 2 doesn't let adding directives dynamically. Create the CoreLinkDirective manually.
|
// Angular 2 doesn't let adding directives dynamically. Create the CoreLinkDirective manually.
|
||||||
const linkDir = new CoreLinkDirective(anchor, this.domUtils, this.utils, this.sitesProvider, this.urlUtils,
|
const linkDir = new CoreLinkDirective(anchor, this.domUtils, this.utils, this.sitesProvider, this.urlUtils,
|
||||||
this.contentLinksHelper, this.navCtrl, this.content, this.svComponent);
|
this.contentLinksHelper, this.navCtrl, this.content, this.svComponent, this.textUtils);
|
||||||
linkDir.capture = true;
|
linkDir.capture = true;
|
||||||
linkDir.ngOnInit();
|
linkDir.ngOnInit();
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ import { CoreUtilsProvider } from '@providers/utils/utils';
|
||||||
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
|
||||||
import { CoreConfigConstants } from '../configconstants';
|
import { CoreConfigConstants } from '../configconstants';
|
||||||
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
import { CoreSplitViewComponent } from '@components/split-view/split-view';
|
||||||
|
import { CoreTextUtilsProvider } from '@providers/utils/text';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Directive to open a link in external browser.
|
* Directive to open a link in external browser.
|
||||||
|
@ -41,7 +42,8 @@ export class CoreLinkDirective implements OnInit {
|
||||||
constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider,
|
constructor(element: ElementRef, private domUtils: CoreDomUtilsProvider, private utils: CoreUtilsProvider,
|
||||||
private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider,
|
private sitesProvider: CoreSitesProvider, private urlUtils: CoreUrlUtilsProvider,
|
||||||
private contentLinksHelper: CoreContentLinksHelperProvider, @Optional() private navCtrl: NavController,
|
private contentLinksHelper: CoreContentLinksHelperProvider, @Optional() private navCtrl: NavController,
|
||||||
@Optional() private content: Content, @Optional() private svComponent: CoreSplitViewComponent) {
|
@Optional() private content: Content, @Optional() private svComponent: CoreSplitViewComponent,
|
||||||
|
private textUtils: CoreTextUtilsProvider) {
|
||||||
// This directive can be added dynamically. In that case, the first param is the anchor HTMLElement.
|
// This directive can be added dynamically. In that case, the first param is the anchor HTMLElement.
|
||||||
this.element = element.nativeElement || element;
|
this.element = element.nativeElement || element;
|
||||||
}
|
}
|
||||||
|
@ -62,12 +64,13 @@ export class CoreLinkDirective implements OnInit {
|
||||||
this.element.addEventListener('click', (event) => {
|
this.element.addEventListener('click', (event) => {
|
||||||
// If the event prevented default action, do nothing.
|
// If the event prevented default action, do nothing.
|
||||||
if (!event.defaultPrevented) {
|
if (!event.defaultPrevented) {
|
||||||
const href = this.element.getAttribute('href');
|
let href = this.element.getAttribute('href');
|
||||||
if (href) {
|
if (href) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
if (this.utils.isTrueOrOne(this.capture)) {
|
if (this.utils.isTrueOrOne(this.capture)) {
|
||||||
|
href = this.textUtils.decodeURI(href);
|
||||||
this.contentLinksHelper.handleLink(href, undefined, navCtrl, true, true).then((treated) => {
|
this.contentLinksHelper.handleLink(href, undefined, navCtrl, true, true).then((treated) => {
|
||||||
if (!treated) {
|
if (!treated) {
|
||||||
this.navigate(href);
|
this.navigate(href);
|
||||||
|
|
Loading…
Reference in New Issue