diff --git a/src/app/app.module.ts b/src/app/app.module.ts index fc46e8adf..a1d70141e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -68,6 +68,7 @@ import { CoreSettingsModule } from '@core/settings/settings.module'; import { CoreSitePluginsModule } from '@core/siteplugins/siteplugins.module'; import { CoreCompileModule } from '@core/compile/compile.module'; import { CoreQuestionModule } from '@core/question/question.module'; +import { CoreCommentsModule } from '@core/comments/comments.module'; // Addon modules. import { AddonBadgesModule } from '@addon/badges/badges.module'; @@ -162,6 +163,7 @@ export const CORE_PROVIDERS: any[] = [ CoreSitePluginsModule, CoreCompileModule, CoreQuestionModule, + CoreCommentsModule, AddonBadgesModule, AddonCalendarModule, AddonCompetencyModule, diff --git a/src/core/comments/comments.module.ts b/src/core/comments/comments.module.ts new file mode 100644 index 000000000..980e458b8 --- /dev/null +++ b/src/core/comments/comments.module.ts @@ -0,0 +1,27 @@ +// (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 { CoreCommentsProvider } from './providers/comments'; + +@NgModule({ + declarations: [ + ], + imports: [ + ], + providers: [ + CoreCommentsProvider + ] +}) +export class CoreCommentsModule {} diff --git a/src/core/comments/components/comments/comments.html b/src/core/comments/components/comments/comments.html new file mode 100644 index 000000000..1b30e656a --- /dev/null +++ b/src/core/comments/components/comments/comments.html @@ -0,0 +1,8 @@ + +
+ {{ 'core.commentscount' | translate : {'$a': commentsCount} }} +
+
+ {{ 'core.commentsnotworking' | translate }} +
+
diff --git a/src/core/comments/components/comments/comments.ts b/src/core/comments/components/comments/comments.ts new file mode 100644 index 000000000..bb9d55458 --- /dev/null +++ b/src/core/comments/components/comments/comments.ts @@ -0,0 +1,71 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, Input } from '@angular/core'; +import { NavParams, NavController } from 'ionic-angular'; +import { CoreCommentsProvider } from '../../providers/comments'; + +/** + * Component that displays the count of comments. + */ +@Component({ + selector: 'core-comments', + templateUrl: 'comments.html', +}) +export class CoreCommentsCommentsComponent { + @Input() contextLevel: string; + @Input() instanceId: number; + @Input() component: string; + @Input() itemId: number; + @Input() area = ''; + @Input() page = 0; + @Input() title?: string; + + commentsLoaded = false; + commentsCount: number; + + constructor(navParams: NavParams, private navCtrl: NavController, private commentsProvider: CoreCommentsProvider) {} + + /** + * View loaded. + */ + ngOnInit(): void { + this.commentsProvider.getComments(this.contextLevel, this.instanceId, this.component, this.itemId, this.area, this.page) + .then((comments) => { + this.commentsCount = comments && comments.length ? comments.length : 0; + }).catch(() => { + this.commentsCount = -1; + }).finally(() => { + this.commentsLoaded = true; + }); + } + + /** + * Opens the comments page. + */ + openComments(): void { + if (this.commentsCount > 0) { + // Open a new state with the interpolated contents. + this.navCtrl.push('CoreCommentsViewerPage', { + contextLevel: this.contextLevel, + instanceId: this.instanceId, + component: this.component, + itemId: this.itemId, + area: this.area, + page: this.page, + title: this.title, + }); + } + } +} diff --git a/src/core/comments/components/components.module.ts b/src/core/comments/components/components.module.ts new file mode 100644 index 000000000..32d57b82e --- /dev/null +++ b/src/core/comments/components/components.module.ts @@ -0,0 +1,41 @@ +// (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 { CoreCommentsCommentsComponent } from './comments/comments'; +import { CoreComponentsModule } from '@components/components.module'; + +@NgModule({ + declarations: [ + CoreCommentsCommentsComponent + ], + imports: [ + CommonModule, + IonicModule, + TranslateModule.forChild(), + CoreComponentsModule + ], + providers: [ + ], + exports: [ + CoreCommentsCommentsComponent + ], + entryComponents: [ + CoreCommentsCommentsComponent + ] +}) +export class CoreCommentsComponentsModule {} diff --git a/src/core/comments/pages/viewer/viewer.html b/src/core/comments/pages/viewer/viewer.html new file mode 100644 index 000000000..ee2c1d2b2 --- /dev/null +++ b/src/core/comments/pages/viewer/viewer.html @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + +

{{ comment.fullname }}

+

{{ comment.time }}

+
+ + + +
+
+
diff --git a/src/core/comments/pages/viewer/viewer.module.ts b/src/core/comments/pages/viewer/viewer.module.ts new file mode 100644 index 000000000..ca5267970 --- /dev/null +++ b/src/core/comments/pages/viewer/viewer.module.ts @@ -0,0 +1,35 @@ +// (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 { CoreCommentsViewerPage } from './viewer'; +import { CoreComponentsModule } from '@components/components.module'; +import { CoreDirectivesModule } from '@directives/directives.module'; +import { CoreCommentsComponentsModule } from '../../components/components.module'; + +@NgModule({ + declarations: [ + CoreCommentsViewerPage + ], + imports: [ + CoreComponentsModule, + CoreDirectivesModule, + CoreCommentsComponentsModule, + IonicPageModule.forChild(CoreCommentsViewerPage), + TranslateModule.forChild() + ], +}) +export class CoreCommentsViewerPageModule {} diff --git a/src/core/comments/pages/viewer/viewer.ts b/src/core/comments/pages/viewer/viewer.ts new file mode 100644 index 000000000..1ce7872a1 --- /dev/null +++ b/src/core/comments/pages/viewer/viewer.ts @@ -0,0 +1,111 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Component, ViewChild } from '@angular/core'; +import { IonicPage, Content, NavParams } from 'ionic-angular'; +import { TranslateService } from '@ngx-translate/core'; +import { CoreSitesProvider } from '@providers/sites'; +import { CoreDomUtilsProvider } from '@providers/utils/dom'; +import { CoreUserProvider } from '@core/user/providers/user'; +import { CoreCommentsProvider } from '../../providers/comments'; + +/** + * Page that displays comments. + */ +@IonicPage({ segment: 'core-comments-viewer' }) +@Component({ + selector: 'page-core-comments-viewer', + templateUrl: 'viewer.html', +}) +export class CoreCommentsViewerPage { + @ViewChild(Content) content: Content; + + comments = []; + commentsLoaded = false; + contextLevel: string; + instanceId: number; + component: string; + itemId: number; + area: string; + page: number; + title: string; + + constructor(navParams: NavParams, sitesProvider: CoreSitesProvider, private userProvider: CoreUserProvider, + private domUtils: CoreDomUtilsProvider, private translate: TranslateService, + private commentsProvider: CoreCommentsProvider) { + + this.contextLevel = navParams.get('contextLevel'); + this.instanceId = navParams.get('instanceId'); + this.component = navParams.get('component'); + this.itemId = navParams.get('itemId'); + this.area = navParams.get('area') || ''; + this.page = navParams.get('page') || 0; + this.title = navParams.get('title') || this.translate.instant('core.comments'); + } + + /** + * View loaded. + */ + ionViewDidLoad(): void { + this.fetchComments().finally(() => { + this.commentsLoaded = true; + }); + } + + /** + * Fetches the comments. + * + * @return {Promise} Resolved when done. + */ + protected fetchComments(): Promise { + // Get comments data. + return this.commentsProvider.getComments(this.contextLevel, this.instanceId, this.component, this.itemId, + this.area, this.page).then((comments) => { + this.comments = comments; + this.comments.sort((a, b) => b.timecreated - a.timecreated); + this.comments.forEach((comment) => { + // Get the user profile image. + this.userProvider.getProfile(comment.userid, undefined, true).then((user) => { + comment.profileimageurl = user.profileimageurl; + }); + }); + }).catch((error) => { + if (error) { + if (this.component == 'assignsubmission_comments') { + this.domUtils.showAlertTranslated('core.notice', 'core.commentsnotworking'); + } else { + this.domUtils.showErrorModal(error); + } + } else { + this.domUtils.showErrorModal(this.translate.instant('core.error') + ': get_comments'); + } + + return Promise.reject(null); + }); + } + + /** + * Refresh the comments. + * + * @param {any} refresher Refresher. + */ + refreshComments(refresher: any): void { + this.commentsProvider.invalidateCommentsData(this.contextLevel, this.instanceId, this.component, + this.itemId, this.area, this.page).finally(() => { + return this.fetchComments().finally(() => { + refresher.complete(); + }); + }); + } +} diff --git a/src/core/comments/providers/comments.ts b/src/core/comments/providers/comments.ts new file mode 100644 index 000000000..db3978029 --- /dev/null +++ b/src/core/comments/providers/comments.ts @@ -0,0 +1,125 @@ +// (C) Copyright 2015 Martin Dougiamas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +import { Injectable } from '@angular/core'; +import { CoreSitesProvider } from '@providers/sites'; + +/** + * Service that provides some features regarding comments. + */ +@Injectable() +export class CoreCommentsProvider { + + protected ROOT_CACHE_KEY = 'mmComments:'; + + constructor(private sitesProvider: CoreSitesProvider) {} + + /** + * Get cache key for get comments data WS calls. + * + * @param {string} contextLevel Contextlevel system, course, user... + * @param {number} instanceId The Instance id of item associated with the context level. + * @param {string} component Component name. + * @param {number} itemId Associated id. + * @param {string} [area=''] String comment area. Default empty. + * @param {number} [page=0] Page number (0 based). Default 0. + * @return {string} Cache key. + */ + protected getCommentsCacheKey(contextLevel: string, instanceId: number, component: string, + itemId: number, area: string = '', page: number = 0): string { + return this.getCommentsPrefixCacheKey(contextLevel, instanceId) + ':' + component + ':' + itemId + ':' + area + ':' + page; + } + + /** + * Get cache key for get comments instance data WS calls. + * + * @param {string} contextLevel Contextlevel system, course, user... + * @param {number} instanceId The Instance id of item associated with the context level. + * @return {string} Cache key. + */ + protected getCommentsPrefixCacheKey(contextLevel: string, instanceId: number): string { + return this.ROOT_CACHE_KEY + 'comments:' + contextLevel + ':' + instanceId; + } + + /** + * Retrieve a list of comments. + * + * @param {string} contextLevel Contextlevel system, course, user... + * @param {number} instanceId The Instance id of item associated with the context level. + * @param {string} component Component name. + * @param {number} itemId Associated id. + * @param {string} [area=''] String comment area. Default empty. + * @param {number} [page=0] Page number (0 based). Default 0. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved with the comments. + */ + getComments(contextLevel: string, instanceId: number, component: string, itemId: number, + area: string = '', page: number = 0, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + const params: any = { + contextlevel: contextLevel, + instanceid: instanceId, + component: component, + itemid: itemId, + area: area, + page: page, + }; + + const preSets = { + cacheKey: this.getCommentsCacheKey(contextLevel, instanceId, component, itemId, area, page) + }; + + return site.read('core_comment_get_comments', params, preSets).then((response) => { + if (response.comments) { + return response.comments; + } + + return Promise.reject(null); + }); + }); + } + + /** + * Invalidates comments data. + * + * @param {string} contextLevel Contextlevel system, course, user... + * @param {number} instanceId The Instance id of item associated with the context level. + * @param {string} component Component name. + * @param {number} itemId Associated id. + * @param {string} [area=''] String comment area. Default empty. + * @param {number} [page=0] Page number (0 based). Default 0. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCommentsData(contextLevel: string, instanceId: number, component: string, itemId: number, + area: string = '', page: number = 0, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKey(this.getCommentsCacheKey(contextLevel, instanceId, component, itemId, area, page)); + }); + } + + /** + * Invalidates all comments data for an instance. + * + * @param {string} contextLevel Contextlevel system, course, user... + * @param {number} instanceId The Instance id of item associated with the context level. + * @param {string} [siteId] Site ID. If not defined, current site. + * @return {Promise} Promise resolved when the data is invalidated. + */ + invalidateCommentsByInstance(contextLevel: string, instanceId: number, siteId?: string): Promise { + return this.sitesProvider.getSite(siteId).then((site) => { + return site.invalidateWsCacheForKeyStartingWith(this.getCommentsPrefixCacheKey(contextLevel, instanceId)); + }); + } +}