From 67be0a6c45640d97076f8aaea5f8c0c7c7568eec Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pau=20Ferrer=20Oca=C3=B1a?= <crazyserver@gmail.com>
Date: Fri, 10 May 2019 11:53:26 +0200
Subject: [PATCH] MOBILE-3002 block: Add Latest announcements block feature

---
 scripts/langindex.json                        |  1 +
 src/addon/block/newsitems/lang/en.json        |  3 +
 src/addon/block/newsitems/newsitems.module.ts | 38 +++++++++
 src/addon/block/newsitems/newsitems.scss      | 26 ++++++
 .../newsitems/providers/block-handler.ts      | 50 +++++++++++
 src/addon/mod/forum/forum.module.ts           |  6 +-
 .../mod/forum/pages/discussion/discussion.ts  |  3 +
 .../mod/forum/providers/index-link-handler.ts | 40 ++++++++-
 .../mod/forum/providers/post-link-handler.ts  | 84 +++++++++++++++++++
 src/app/app.module.ts                         |  2 +
 src/assets/lang/en.json                       |  1 +
 11 files changed, 252 insertions(+), 2 deletions(-)
 create mode 100644 src/addon/block/newsitems/lang/en.json
 create mode 100644 src/addon/block/newsitems/newsitems.module.ts
 create mode 100644 src/addon/block/newsitems/newsitems.scss
 create mode 100644 src/addon/block/newsitems/providers/block-handler.ts
 create mode 100644 src/addon/mod/forum/providers/post-link-handler.ts

diff --git a/scripts/langindex.json b/scripts/langindex.json
index a700c40cd..20feed1fe 100644
--- a/scripts/langindex.json
+++ b/scripts/langindex.json
@@ -43,6 +43,7 @@
   "addon.block_myoverview.past": "block_myoverview",
   "addon.block_myoverview.pluginname": "block_myoverview",
   "addon.block_myoverview.title": "block_myoverview",
+  "addon.block_newsitems.pluginname": "block_news_items",
   "addon.block_onlineusers.pluginname": "block_online_users",
   "addon.block_privatefiles.pluginname": "block_private_files",
   "addon.block_recentlyaccessedcourses.nocourses": "block_recentlyaccessedcourses",
diff --git a/src/addon/block/newsitems/lang/en.json b/src/addon/block/newsitems/lang/en.json
new file mode 100644
index 000000000..83b981297
--- /dev/null
+++ b/src/addon/block/newsitems/lang/en.json
@@ -0,0 +1,3 @@
+{
+    "pluginname": "Latest announcements"
+}
\ No newline at end of file
diff --git a/src/addon/block/newsitems/newsitems.module.ts b/src/addon/block/newsitems/newsitems.module.ts
new file mode 100644
index 000000000..a3364ea5e
--- /dev/null
+++ b/src/addon/block/newsitems/newsitems.module.ts
@@ -0,0 +1,38 @@
+// (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 { IonicModule } from 'ionic-angular';
+import { TranslateModule } from '@ngx-translate/core';
+import { CoreBlockDelegate } from '@core/block/providers/delegate';
+import { AddonBlockNewsItemsHandler } from './providers/block-handler';
+
+@NgModule({
+    declarations: [
+    ],
+    imports: [
+        IonicModule,
+        TranslateModule.forChild()
+    ],
+    exports: [
+    ],
+    providers: [
+        AddonBlockNewsItemsHandler
+    ]
+})
+export class AddonBlockNewsItemsModule {
+    constructor(blockDelegate: CoreBlockDelegate, blockHandler: AddonBlockNewsItemsHandler) {
+        blockDelegate.registerHandler(blockHandler);
+    }
+}
diff --git a/src/addon/block/newsitems/newsitems.scss b/src/addon/block/newsitems/newsitems.scss
new file mode 100644
index 000000000..8b0c46287
--- /dev/null
+++ b/src/addon/block/newsitems/newsitems.scss
@@ -0,0 +1,26 @@
+.addon-block-news-items core-block-pre-rendered {
+    .core-block-content {
+        .unlist {
+            list-style-type: none;
+            @include margin-horizontal(0);
+            -webkit-padding-start: 0;
+
+            li.post {
+                padding-bottom: 16px;
+            }
+            li.post:last-child {
+                padding-bottom: 0;
+            }
+        }
+    }
+
+    // Hide RSS link.
+    .core-block-footer {
+        a {
+            display: none;
+        }
+        a:first-child {
+            display: inline;
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/addon/block/newsitems/providers/block-handler.ts b/src/addon/block/newsitems/providers/block-handler.ts
new file mode 100644
index 000000000..9b6c15cb8
--- /dev/null
+++ b/src/addon/block/newsitems/providers/block-handler.ts
@@ -0,0 +1,50 @@
+// (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 { TranslateService } from '@ngx-translate/core';
+import { CoreBlockHandlerData } from '@core/block/providers/delegate';
+import { CoreBlockPreRenderedComponent } from '@core/block/components/pre-rendered-block/pre-rendered-block';
+import { CoreBlockBaseHandler } from '@core/block/classes/base-block-handler';
+
+/**
+ * Block handler.
+ */
+@Injectable()
+export class AddonBlockNewsItemsHandler extends CoreBlockBaseHandler {
+    name = 'AddonBlockNewsItems';
+    blockName = 'news_items';
+
+    constructor(private translate: TranslateService) {
+        super();
+    }
+
+    /**
+     * Returns the data needed to render the block.
+     *
+     * @param {Injector} injector Injector.
+     * @param {any} block The block to render.
+     * @param {string} contextLevel The context where the block will be used.
+     * @param {number} instanceId The instance ID associated with the context level.
+     * @return {CoreBlockHandlerData|Promise<CoreBlockHandlerData>} Data or promise resolved with the data.
+     */
+    getDisplayData(injector: Injector, block: any, contextLevel: string, instanceId: number)
+            : CoreBlockHandlerData | Promise<CoreBlockHandlerData> {
+        return {
+            title: this.translate.instant('addon.block_newsitems.pluginname'),
+            class: 'addon-block-news-items',
+            component: CoreBlockPreRenderedComponent
+        };
+    }
+}
diff --git a/src/addon/mod/forum/forum.module.ts b/src/addon/mod/forum/forum.module.ts
index 215c343e6..94fe30257 100644
--- a/src/addon/mod/forum/forum.module.ts
+++ b/src/addon/mod/forum/forum.module.ts
@@ -28,6 +28,7 @@ import { AddonModForumSyncCronHandler } from './providers/sync-cron-handler';
 import { AddonModForumIndexLinkHandler } from './providers/index-link-handler';
 import { AddonModForumDiscussionLinkHandler } from './providers/discussion-link-handler';
 import { AddonModForumListLinkHandler } from './providers/list-link-handler';
+import { AddonModForumPostLinkHandler } from './providers/post-link-handler';
 import { AddonModForumPushClickHandler } from './providers/push-click-handler';
 import { AddonModForumComponentsModule } from './components/components.module';
 import { CoreUpdateManagerProvider } from '@providers/update-manager';
@@ -56,6 +57,7 @@ export const ADDON_MOD_FORUM_PROVIDERS: any[] = [
         AddonModForumSyncCronHandler,
         AddonModForumIndexLinkHandler,
         AddonModForumListLinkHandler,
+        AddonModForumPostLinkHandler,
         AddonModForumDiscussionLinkHandler,
         AddonModForumPushClickHandler
     ]
@@ -66,7 +68,8 @@ export class AddonModForumModule {
             cronDelegate: CoreCronDelegate, syncHandler: AddonModForumSyncCronHandler, linksDelegate: CoreContentLinksDelegate,
             indexHandler: AddonModForumIndexLinkHandler, discussionHandler: AddonModForumDiscussionLinkHandler,
             updateManager: CoreUpdateManagerProvider, listLinkHandler: AddonModForumListLinkHandler,
-            pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModForumPushClickHandler) {
+            pushNotificationsDelegate: CorePushNotificationsDelegate, pushClickHandler: AddonModForumPushClickHandler,
+            postLinkHandler: AddonModForumPostLinkHandler) {
 
         moduleDelegate.registerHandler(moduleHandler);
         prefetchDelegate.registerHandler(prefetchHandler);
@@ -74,6 +77,7 @@ export class AddonModForumModule {
         linksDelegate.registerHandler(indexHandler);
         linksDelegate.registerHandler(discussionHandler);
         linksDelegate.registerHandler(listLinkHandler);
+        linksDelegate.registerHandler(postLinkHandler);
         pushNotificationsDelegate.registerClickHandler(pushClickHandler);
 
         // Allow migrating the tables from the old app to the new schema.
diff --git a/src/addon/mod/forum/pages/discussion/discussion.ts b/src/addon/mod/forum/pages/discussion/discussion.ts
index f8514dd5f..68598a18e 100644
--- a/src/addon/mod/forum/pages/discussion/discussion.ts
+++ b/src/addon/mod/forum/pages/discussion/discussion.ts
@@ -354,6 +354,9 @@ export class AddonModForumDiscussionPage implements OnDestroy {
                     return Promise.reject('Invalid forum discussion.');
                 }
 
+                this.defaultSubject = this.translate.instant('addon.mod_forum.re') + ' ' + this.discussion.subject;
+                this.replyData.subject = this.defaultSubject;
+
                 if (this.discussion.userfullname && this.discussion.parent == 0 && this.forum.type == 'single') {
                     // Hide author for first post and type single.
                     this.discussion.userfullname = null;
diff --git a/src/addon/mod/forum/providers/index-link-handler.ts b/src/addon/mod/forum/providers/index-link-handler.ts
index 4beb1226f..b975f449c 100644
--- a/src/addon/mod/forum/providers/index-link-handler.ts
+++ b/src/addon/mod/forum/providers/index-link-handler.ts
@@ -16,6 +16,9 @@ import { Injectable } from '@angular/core';
 import { CoreContentLinksModuleIndexHandler } from '@core/contentlinks/classes/module-index-handler';
 import { CoreCourseHelperProvider } from '@core/course/providers/helper';
 import { AddonModForumProvider } from './forum';
+import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
+import { CoreCourseProvider } from '@core/course/providers/course';
+import { CoreDomUtilsProvider } from '@providers/utils/dom';
 
 /**
  * Handler to treat links to forum index.
@@ -24,8 +27,12 @@ import { AddonModForumProvider } from './forum';
 export class AddonModForumIndexLinkHandler extends CoreContentLinksModuleIndexHandler {
     name = 'AddonModForumIndexLinkHandler';
 
-    constructor(courseHelper: CoreCourseHelperProvider, protected forumProvider: AddonModForumProvider) {
+    constructor(courseHelper: CoreCourseHelperProvider, protected forumProvider: AddonModForumProvider,
+            private courseProvider: CoreCourseProvider, private domUtils: CoreDomUtilsProvider) {
         super(courseHelper, 'AddonModForum', 'forum');
+
+        // Match the view.php URL with an id param.
+        this.pattern = new RegExp('\/mod\/forum\/view\.php.*([\&\?](f|id)=\\d+)');
     }
 
     /**
@@ -41,4 +48,35 @@ export class AddonModForumIndexLinkHandler extends CoreContentLinksModuleIndexHa
     isEnabled(siteId: string, url: string, params: any, courseId?: number): boolean | Promise<boolean> {
         return true;
     }
+
+    /**
+     * Get the list of actions for a link (url).
+     *
+     * @param {string[]} siteIds List of sites the URL belongs to.
+     * @param {string} url The URL to treat.
+     * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+     * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+     * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
+     */
+    getActions(siteIds: string[], url: string, params: any, courseId?: number):
+            CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
+
+        if (typeof params.f != 'undefined') {
+            return [{
+                action: (siteId, navCtrl?): void => {
+                    const modal = this.domUtils.showModalLoading(),
+                        forumId = parseInt(params.f, 10);
+
+                    this.courseProvider.getModuleBasicInfoByInstance(forumId, 'forum', siteId).then((module) => {
+                        this.courseHelper.navigateToModule(parseInt(module.id, 10), siteId, module.course);
+                    }).finally(() => {
+                        // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
+                        modal.dismiss();
+                    });
+                }
+            }];
+        }
+
+        return super.getActions(siteIds, url, params, courseId);
+    }
 }
diff --git a/src/addon/mod/forum/providers/post-link-handler.ts b/src/addon/mod/forum/providers/post-link-handler.ts
new file mode 100644
index 000000000..73d691dc1
--- /dev/null
+++ b/src/addon/mod/forum/providers/post-link-handler.ts
@@ -0,0 +1,84 @@
+// (C) Copyright 2015 Martin Dougiamas
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import { Injectable } from '@angular/core';
+import { CoreContentLinksHandlerBase } from '@core/contentlinks/classes/base-handler';
+import { CoreContentLinksAction } from '@core/contentlinks/providers/delegate';
+import { CoreContentLinksHelperProvider } from '@core/contentlinks/providers/helper';
+import { CoreCourseProvider } from '@core/course/providers/course';
+import { CoreDomUtilsProvider } from '@providers/utils/dom';
+
+/**
+ * Content links handler for forum new discussion.
+ * Match mod/forum/post.php?forum=6 with a valid data.
+ */
+@Injectable()
+export class AddonModForumPostLinkHandler extends CoreContentLinksHandlerBase {
+    name = 'AddonModForumPostLinkHandler';
+    featureName = 'CoreCourseModuleDelegate_AddonModForum';
+    pattern = /\/mod\/forum\/post\.php.*([\?\&](forum)=\d+)/;
+
+    constructor(private linkHelper: CoreContentLinksHelperProvider, private courseProvider: CoreCourseProvider,
+            private domUtils: CoreDomUtilsProvider) {
+        super();
+    }
+
+    /**
+     * Get the list of actions for a link (url).
+     *
+     * @param {string[]} siteIds List of sites the URL belongs to.
+     * @param {string} url The URL to treat.
+     * @param {any} params The params of the URL. E.g. 'mysite.com?id=1' -> {id: 1}
+     * @param {number} [courseId] Course ID related to the URL. Optional but recommended.
+     * @return {CoreContentLinksAction[]|Promise<CoreContentLinksAction[]>} List of (or promise resolved with list of) actions.
+     */
+    getActions(siteIds: string[], url: string, params: any, courseId?: number):
+            CoreContentLinksAction[] | Promise<CoreContentLinksAction[]> {
+
+        return [{
+            action: (siteId, navCtrl?): void => {
+                const modal = this.domUtils.showModalLoading(),
+                    forumId = parseInt(params.forum, 10);
+
+                this.courseProvider.getModuleBasicInfoByInstance(forumId, 'forum', siteId).then((module) => {
+                    const pageParams = {
+                        courseId: module.course,
+                        cmId: module.id,
+                        forumId: module.instance,
+                        timeCreated: 0,
+                    };
+
+                    return this.linkHelper.goInSite(navCtrl, 'AddonModForumNewDiscussionPage', pageParams, siteId);
+                }).finally(() => {
+                    // Just in case. In fact we need to dismiss the modal before showing a toast or error message.
+                    modal.dismiss();
+                });
+            }
+        }];
+    }
+
+    /**
+     * 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 typeof params.forum != 'undefined';
+    }
+}
diff --git a/src/app/app.module.ts b/src/app/app.module.ts
index 8ce3a1796..4d4743294 100644
--- a/src/app/app.module.ts
+++ b/src/app/app.module.ts
@@ -97,6 +97,7 @@ import { AddonBlockCommentsModule } from '@addon/block/comments/comments.module'
 import { AddonBlockCompletionStatusModule } from '@addon/block/completionstatus/completionstatus.module';
 import { AddonBlockHtmlModule } from '@addon/block/html/html.module';
 import { AddonBlockMyOverviewModule } from '@addon/block/myoverview/myoverview.module';
+import { AddonBlockNewsItemsModule } from '@addon/block/newsitems/newsitems.module';
 import { AddonBlockOnlineUsersModule } from '@addon/block/onlineusers/onlineusers.module';
 import { AddonBlockLearningPlansModule } from '@addon/block/learningplans/learningplans.module';
 import { AddonBlockPrivateFilesModule } from '@addon/block/privatefiles/privatefiles.module';
@@ -229,6 +230,7 @@ export const CORE_PROVIDERS: any[] = [
         AddonBlockHtmlModule,
         AddonBlockLearningPlansModule,
         AddonBlockMyOverviewModule,
+        AddonBlockNewsItemsModule,
         AddonBlockOnlineUsersModule,
         AddonBlockPrivateFilesModule,
         AddonBlockSiteMainMenuModule,
diff --git a/src/assets/lang/en.json b/src/assets/lang/en.json
index 4d3f6b00e..88734646f 100644
--- a/src/assets/lang/en.json
+++ b/src/assets/lang/en.json
@@ -43,6 +43,7 @@
     "addon.block_myoverview.past": "Past",
     "addon.block_myoverview.pluginname": "Course overview",
     "addon.block_myoverview.title": "Course name",
+    "addon.block_newsitems.pluginname": "Latest announcements",
     "addon.block_onlineusers.pluginname": "Online users",
     "addon.block_privatefiles.pluginname": "Private files",
     "addon.block_recentlyaccessedcourses.nocourses": "No recent courses",