forked from CIT/Vmeda.Online
		
	MOBILE-3627 blog: Add blog functionality
This commit is contained in:
		
							parent
							
								
									8471b549d1
								
							
						
					
					
						commit
						8097b4e1a8
					
				@ -26,11 +26,13 @@ import { AddonMessagesModule } from './messages/messages.module';
 | 
			
		||||
import { AddonModModule } from './mod/mod.module';
 | 
			
		||||
import { AddonQbehaviourModule } from './qbehaviour/qbehaviour.module';
 | 
			
		||||
import { AddonQtypeModule } from './qtype/qtype.module';
 | 
			
		||||
import { AddonBlogModule } from './blog/blog.module';
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        AddonBlockModule,
 | 
			
		||||
        AddonBadgesModule,
 | 
			
		||||
        AddonBlogModule,
 | 
			
		||||
        AddonCalendarModule,
 | 
			
		||||
        AddonMessagesModule,
 | 
			
		||||
        AddonPrivateFilesModule,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										52
									
								
								src/addons/blog/blog-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/addons/blog/blog-lazy.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { Injector, NgModule } from '@angular/core';
 | 
			
		||||
import { RouterModule, ROUTES, Routes } from '@angular/router';
 | 
			
		||||
 | 
			
		||||
import { CoreSharedModule } from '@/core/shared.module';
 | 
			
		||||
import { AddonBlogEntriesPage } from './pages/entries/entries';
 | 
			
		||||
import { CoreCommentsComponentsModule } from '@features/comments/components/components.module';
 | 
			
		||||
 | 
			
		||||
import { CoreTagComponentsModule } from '@features/tag/components/components.module';
 | 
			
		||||
import { buildTabMainRoutes } from '@features/mainmenu/mainmenu-tab-routing.module';
 | 
			
		||||
 | 
			
		||||
function buildRoutes(injector: Injector): Routes {
 | 
			
		||||
    return [
 | 
			
		||||
        ...buildTabMainRoutes(injector, {
 | 
			
		||||
            component: AddonBlogEntriesPage,
 | 
			
		||||
        }),
 | 
			
		||||
    ];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreSharedModule,
 | 
			
		||||
        CoreCommentsComponentsModule,
 | 
			
		||||
        CoreTagComponentsModule,
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [RouterModule],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: ROUTES,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [Injector],
 | 
			
		||||
            useFactory: buildRoutes,
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
    declarations: [
 | 
			
		||||
        AddonBlogEntriesPage,
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonBlogLazyModule {}
 | 
			
		||||
							
								
								
									
										65
									
								
								src/addons/blog/blog.module.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/addons/blog/blog.module.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,65 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { APP_INITIALIZER, NgModule, Type } from '@angular/core';
 | 
			
		||||
import { Routes } from '@angular/router';
 | 
			
		||||
import { CoreContentLinksDelegate } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreCourseIndexRoutingModule } from '@features/course/pages/index/index-routing.module';
 | 
			
		||||
import { CoreCourseOptionsDelegate } from '@features/course/services/course-options-delegate';
 | 
			
		||||
import { CoreMainMenuRoutingModule } from '@features/mainmenu/mainmenu-routing.module';
 | 
			
		||||
import { CoreMainMenuTabRoutingModule } from '@features/mainmenu/mainmenu-tab-routing.module';
 | 
			
		||||
import { CoreMainMenuDelegate } from '@features/mainmenu/services/mainmenu-delegate';
 | 
			
		||||
import { CoreTagAreaDelegate } from '@features/tag/services/tag-area-delegate';
 | 
			
		||||
import { CoreUserDelegate } from '@features/user/services/user-delegate';
 | 
			
		||||
import { AddonBlogProvider } from './services/blog';
 | 
			
		||||
import { AddonBlogCourseOptionHandler } from './services/handlers/course-option';
 | 
			
		||||
import { AddonBlogIndexLinkHandler } from './services/handlers/index-link';
 | 
			
		||||
import { AddonBlogMainMenuHandler, AddonBlogMainMenuHandlerService } from './services/handlers/mainmenu';
 | 
			
		||||
import { AddonBlogTagAreaHandler } from './services/handlers/tag-area';
 | 
			
		||||
import { AddonBlogUserHandler } from './services/handlers/user';
 | 
			
		||||
 | 
			
		||||
export const ADDON_BLOG_SERVICES: Type<unknown>[] = [
 | 
			
		||||
    AddonBlogProvider,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const routes: Routes = [
 | 
			
		||||
    {
 | 
			
		||||
        path: AddonBlogMainMenuHandlerService.PAGE_NAME,
 | 
			
		||||
        loadChildren: () => import('@addons/blog/blog-lazy.module').then(m => m.AddonBlogLazyModule),
 | 
			
		||||
    },
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
@NgModule({
 | 
			
		||||
    imports: [
 | 
			
		||||
        CoreMainMenuTabRoutingModule.forChild(routes),
 | 
			
		||||
        CoreMainMenuRoutingModule.forChild({ children: routes }),
 | 
			
		||||
        CoreCourseIndexRoutingModule.forChild({ children: routes }),
 | 
			
		||||
    ],
 | 
			
		||||
    exports: [CoreMainMenuRoutingModule],
 | 
			
		||||
    providers: [
 | 
			
		||||
        {
 | 
			
		||||
            provide: APP_INITIALIZER,
 | 
			
		||||
            multi: true,
 | 
			
		||||
            deps: [],
 | 
			
		||||
            useFactory: () => async () => {
 | 
			
		||||
                CoreContentLinksDelegate.registerHandler(AddonBlogIndexLinkHandler.instance);
 | 
			
		||||
                CoreMainMenuDelegate.registerHandler(AddonBlogMainMenuHandler.instance);
 | 
			
		||||
                CoreUserDelegate.registerHandler(AddonBlogUserHandler.instance);
 | 
			
		||||
                CoreTagAreaDelegate.registerHandler(AddonBlogTagAreaHandler.instance);
 | 
			
		||||
                CoreCourseOptionsDelegate.registerHandler(AddonBlogCourseOptionHandler.instance);
 | 
			
		||||
            },
 | 
			
		||||
        },
 | 
			
		||||
    ],
 | 
			
		||||
})
 | 
			
		||||
export class AddonBlogModule {}
 | 
			
		||||
							
								
								
									
										12
									
								
								src/addons/blog/lang.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								src/addons/blog/lang.json
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
{
 | 
			
		||||
    "blog": "Blog",
 | 
			
		||||
    "blogentries": "Blog entries",
 | 
			
		||||
    "errorloadentries": "Error loading blog entries.",
 | 
			
		||||
    "linktooriginalentry": "Link to original blog entry",
 | 
			
		||||
    "noentriesyet": "No visible entries here",
 | 
			
		||||
    "publishtonoone": "Yourself (draft)",
 | 
			
		||||
    "publishtosite": "Anyone on this site",
 | 
			
		||||
    "publishtoworld": "Anyone in the world",
 | 
			
		||||
    "showonlyyourentries": "Show only your entries",
 | 
			
		||||
    "siteblogheading": "Site blog"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										82
									
								
								src/addons/blog/pages/entries/entries.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/addons/blog/pages/entries/entries.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,82 @@
 | 
			
		||||
<ion-header>
 | 
			
		||||
    <ion-toolbar>
 | 
			
		||||
        <ion-buttons slot="start">
 | 
			
		||||
            <ion-back-button [attr.aria-label]="'core.back' | translate"></ion-back-button>
 | 
			
		||||
        </ion-buttons>
 | 
			
		||||
        <ion-title>{{ title | translate }}</ion-title>
 | 
			
		||||
        <ion-buttons slot="end"></ion-buttons>
 | 
			
		||||
    </ion-toolbar>
 | 
			
		||||
</ion-header>
 | 
			
		||||
<ion-content>
 | 
			
		||||
    <ion-refresher slot="fixed" [disabled]="!loaded" (ionRefresh)="refresh($event)">
 | 
			
		||||
        <ion-refresher-content pullingText="{{ 'core.pulltorefresh' | translate }}"></ion-refresher-content>
 | 
			
		||||
    </ion-refresher>
 | 
			
		||||
    <core-loading [hideUntil]="loaded" class="core-loading-center">
 | 
			
		||||
        <ion-item *ngIf="showMyEntriesToggle">
 | 
			
		||||
            <ion-label>{{ 'addon.blog.showonlyyourentries' | translate }}</ion-label>
 | 
			
		||||
            <ion-toggle [(ngModel)]="onlyMyEntries" (ionChange)="onlyMyEntriesToggleChanged(onlyMyEntries)"></ion-toggle>
 | 
			
		||||
        </ion-item>
 | 
			
		||||
        <core-empty-box *ngIf="entries && entries.length == 0" icon="far-newspaper"
 | 
			
		||||
            [message]="'addon.blog.noentriesyet' | translate">
 | 
			
		||||
        </core-empty-box>
 | 
			
		||||
        <ng-container *ngFor="let entry of entries">
 | 
			
		||||
            <ion-card *ngIf="!onlyMyEntries || entry.userid == currentUserId">
 | 
			
		||||
                <ion-item class="ion-text-wrap">
 | 
			
		||||
                    <core-user-avatar [user]="entry.user" slot="start" [courseId]="entry.courseid"></core-user-avatar>
 | 
			
		||||
                    <ion-label>
 | 
			
		||||
                        <h2>
 | 
			
		||||
                            <core-format-text [text]="entry.subject" [contextLevel]="contextLevel"
 | 
			
		||||
                                [contextInstanceId]="contextInstanceId">
 | 
			
		||||
                            </core-format-text>
 | 
			
		||||
                            <ion-note class="ion-float-end ion-padding-left ion-text-end">
 | 
			
		||||
                                {{ 'addon.blog.' + entry.publishTranslated! | translate}}
 | 
			
		||||
                            </ion-note>
 | 
			
		||||
                        </h2>
 | 
			
		||||
                        <p>
 | 
			
		||||
                            <ion-note class="ion-float-end ion-padding-left ion-text-end">
 | 
			
		||||
                                {{entry.created | coreDateDayOrTime}}
 | 
			
		||||
                            </ion-note>
 | 
			
		||||
                            {{entry.user && entry.user!.fullname}}
 | 
			
		||||
                        </p>
 | 
			
		||||
                    </ion-label>
 | 
			
		||||
                </ion-item>
 | 
			
		||||
                <ion-card-content>
 | 
			
		||||
                    <ion-item>
 | 
			
		||||
                        <ion-label>
 | 
			
		||||
                            <core-format-text [text]="entry.summary" [component]="this.component" [componentId]="entry.id"
 | 
			
		||||
                                [contextLevel]="contextLevel" [contextInstanceId]="contextInstanceId">
 | 
			
		||||
                            </core-format-text>
 | 
			
		||||
                        </ion-label>
 | 
			
		||||
                    </ion-item>
 | 
			
		||||
                    <ion-item class="ion-text-wrap" *ngIf="tagsEnabled && entry.tags && entry.tags!.length > 0">
 | 
			
		||||
                        <ion-label>
 | 
			
		||||
                            <div slot="start">{{ 'core.tag.tags' | translate }}:</div>
 | 
			
		||||
                            <core-tag-list [tags]="entry.tags"></core-tag-list>
 | 
			
		||||
                        </ion-label>
 | 
			
		||||
                    </ion-item>
 | 
			
		||||
                    <ion-item *ngIf="commentsEnabled" detail>
 | 
			
		||||
                        <ion-label>
 | 
			
		||||
                            <core-comments [component]="this.component" [itemId]="entry.id" area="format_blog"
 | 
			
		||||
                                [instanceId]="entry.userid" contextLevel="user">
 | 
			
		||||
                            </core-comments>
 | 
			
		||||
                        </ion-label>
 | 
			
		||||
                    </ion-item>
 | 
			
		||||
                    <core-file *ngFor="let file of entry.attachmentfiles" [file]="file" [component]="this.component"
 | 
			
		||||
                        [componentId]="entry.id">
 | 
			
		||||
                    </core-file>
 | 
			
		||||
                    <ion-item *ngIf="entry.uniquehash" [href]="entry.uniquehash" core-link>
 | 
			
		||||
                        <ion-label>{{ 'addon.blog.linktooriginalentry' | translate }}</ion-label>
 | 
			
		||||
                    </ion-item>
 | 
			
		||||
                </ion-card-content>
 | 
			
		||||
                <ion-row class="ion-text-center">
 | 
			
		||||
                    <ion-col *ngIf="entry.lastmodified > entry.created">
 | 
			
		||||
                        <ion-note>
 | 
			
		||||
                            <ion-icon name="fas-clock"></ion-icon> {{entry.lastmodified | coreTimeAgo}}
 | 
			
		||||
                        </ion-note>
 | 
			
		||||
                    </ion-col>
 | 
			
		||||
                </ion-row>
 | 
			
		||||
            </ion-card>
 | 
			
		||||
        </ng-container>
 | 
			
		||||
        <core-infinite-loading [enabled]="canLoadMore" (action)="loadMore($event)" [error]="loadMoreError"></core-infinite-loading>
 | 
			
		||||
    </core-loading>
 | 
			
		||||
</ion-content>
 | 
			
		||||
							
								
								
									
										288
									
								
								src/addons/blog/pages/entries/entries.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										288
									
								
								src/addons/blog/pages/entries/entries.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,288 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { ContextLevel } from '@/core/constants';
 | 
			
		||||
import { AddonBlog, AddonBlogFilter, AddonBlogPost, AddonBlogProvider } from '@addons/blog/services/blog';
 | 
			
		||||
import { Component, OnInit } from '@angular/core';
 | 
			
		||||
import { CoreComments } from '@features/comments/services/comments';
 | 
			
		||||
import { CoreTag } from '@features/tag/services/tag';
 | 
			
		||||
import { CoreUser, CoreUserProfile } from '@features/user/services/user';
 | 
			
		||||
import { IonRefresher } from '@ionic/angular';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
import { CoreTextUtils } from '@services/utils/text';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Page that displays the list of blog entries.
 | 
			
		||||
 */
 | 
			
		||||
@Component({
 | 
			
		||||
    selector: 'page-addon-blog-entries',
 | 
			
		||||
    templateUrl: 'entries.html',
 | 
			
		||||
})
 | 
			
		||||
export class AddonBlogEntriesPage implements OnInit {
 | 
			
		||||
 | 
			
		||||
    title = '';
 | 
			
		||||
 | 
			
		||||
    protected filter: AddonBlogFilter = {};
 | 
			
		||||
    protected pageLoaded = 0;
 | 
			
		||||
    protected userPageLoaded = 0;
 | 
			
		||||
    protected canLoadMoreEntries = false;
 | 
			
		||||
    protected canLoadMoreUserEntries = true;
 | 
			
		||||
    protected siteHomeId: number;
 | 
			
		||||
 | 
			
		||||
    loaded = false;
 | 
			
		||||
    canLoadMore = false;
 | 
			
		||||
    loadMoreError = false;
 | 
			
		||||
    entries: AddonBlogPostFormatted[] = [];
 | 
			
		||||
    currentUserId: number;
 | 
			
		||||
    showMyEntriesToggle = false;
 | 
			
		||||
    onlyMyEntries = false;
 | 
			
		||||
    component = AddonBlogProvider.COMPONENT;
 | 
			
		||||
    commentsEnabled = false;
 | 
			
		||||
    tagsEnabled = false;
 | 
			
		||||
    contextLevel: ContextLevel = ContextLevel.SYSTEM;
 | 
			
		||||
    contextInstanceId = 0;
 | 
			
		||||
 | 
			
		||||
    constructor() {
 | 
			
		||||
        this.currentUserId = CoreSites.getCurrentSiteUserId();
 | 
			
		||||
        this.siteHomeId = CoreSites.getCurrentSiteHomeId();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * View loaded.
 | 
			
		||||
     */
 | 
			
		||||
    async ngOnInit(): Promise<void> {
 | 
			
		||||
        const userId = CoreNavigator.getRouteNumberParam('userId');
 | 
			
		||||
        const courseId = CoreNavigator.getRouteNumberParam('courseId');
 | 
			
		||||
        const cmId = CoreNavigator.getRouteNumberParam('cmId');
 | 
			
		||||
        const entryId = CoreNavigator.getRouteNumberParam('entryId');
 | 
			
		||||
        const groupId = CoreNavigator.getRouteNumberParam('groupId');
 | 
			
		||||
        const tagId = CoreNavigator.getRouteNumberParam('tagId');
 | 
			
		||||
 | 
			
		||||
        if (!userId && !courseId && !cmId && !entryId && !groupId && !tagId) {
 | 
			
		||||
            this.title = 'addon.blog.siteblogheading';
 | 
			
		||||
        } else {
 | 
			
		||||
            this.title = 'addon.blog.blogentries';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (userId) {
 | 
			
		||||
            this.filter.userid = userId;
 | 
			
		||||
        }
 | 
			
		||||
        this.showMyEntriesToggle = !userId;
 | 
			
		||||
 | 
			
		||||
        if (courseId) {
 | 
			
		||||
            this.filter.courseid = courseId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (cmId) {
 | 
			
		||||
            this.filter.cmid = cmId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (entryId) {
 | 
			
		||||
            this.filter.entryid = entryId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (groupId) {
 | 
			
		||||
            this.filter.groupid = groupId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (tagId) {
 | 
			
		||||
            this.filter.tagid = tagId;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // Calculate the context level.
 | 
			
		||||
        if (userId && !courseId && !cmId) {
 | 
			
		||||
            this.contextLevel = ContextLevel.USER;
 | 
			
		||||
            this.contextInstanceId = userId;
 | 
			
		||||
        } else if (courseId && courseId != this.siteHomeId) {
 | 
			
		||||
            this.contextLevel = ContextLevel.COURSE;
 | 
			
		||||
            this.contextInstanceId = courseId;
 | 
			
		||||
        } else {
 | 
			
		||||
            this.contextLevel = ContextLevel.SYSTEM;
 | 
			
		||||
            this.contextInstanceId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.commentsEnabled = !CoreComments.areCommentsDisabledInSite();
 | 
			
		||||
        this.tagsEnabled = CoreTag.areTagsAvailableInSite();
 | 
			
		||||
 | 
			
		||||
        await this.fetchEntries();
 | 
			
		||||
 | 
			
		||||
        CoreUtils.ignoreErrors(AddonBlog.logView(this.filter));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Fetch blog entries.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresh Empty events array first.
 | 
			
		||||
     * @return Promise with the entries.
 | 
			
		||||
     */
 | 
			
		||||
    protected async fetchEntries(refresh: boolean = false): Promise<void> {
 | 
			
		||||
        this.loadMoreError = false;
 | 
			
		||||
 | 
			
		||||
        if (refresh) {
 | 
			
		||||
            this.pageLoaded = 0;
 | 
			
		||||
            this.userPageLoaded = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const loadPage = this.onlyMyEntries ? this.userPageLoaded : this.pageLoaded;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            const result = await AddonBlog.getEntries(this.filter, loadPage);
 | 
			
		||||
 | 
			
		||||
            const promises = result.entries.map(async (entry: AddonBlogPostFormatted) => {
 | 
			
		||||
                switch (entry.publishstate) {
 | 
			
		||||
                    case 'draft':
 | 
			
		||||
                        entry.publishTranslated = 'publishtonoone';
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'site':
 | 
			
		||||
                        entry.publishTranslated = 'publishtosite';
 | 
			
		||||
                        break;
 | 
			
		||||
                    case 'public':
 | 
			
		||||
                        entry.publishTranslated = 'publishtoworld';
 | 
			
		||||
                        break;
 | 
			
		||||
                    default:
 | 
			
		||||
                        entry.publishTranslated = 'privacy:unknown';
 | 
			
		||||
                        break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                // Calculate the context. This code was inspired by calendar events, Moodle doesn't do this for blogs.
 | 
			
		||||
                if (entry.moduleid || entry.coursemoduleid) {
 | 
			
		||||
                    entry.contextLevel = ContextLevel.MODULE;
 | 
			
		||||
                    entry.contextInstanceId = entry.moduleid || entry.coursemoduleid;
 | 
			
		||||
                } else if (entry.courseid) {
 | 
			
		||||
                    entry.contextLevel = ContextLevel.COURSE;
 | 
			
		||||
                    entry.contextInstanceId = entry.courseid;
 | 
			
		||||
                } else {
 | 
			
		||||
                    entry.contextLevel = ContextLevel.USER;
 | 
			
		||||
                    entry.contextInstanceId = entry.userid;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                entry.summary = CoreTextUtils.instance.replacePluginfileUrls(entry.summary, entry.summaryfiles || []);
 | 
			
		||||
 | 
			
		||||
                return CoreUser.getProfile(entry.userid, entry.courseid, true).then((user) => {
 | 
			
		||||
                    entry.user = user;
 | 
			
		||||
 | 
			
		||||
                    return;
 | 
			
		||||
                }).catch(() => {
 | 
			
		||||
                    // Ignore errors.
 | 
			
		||||
                });
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            if (refresh) {
 | 
			
		||||
                this.entries = result.entries;
 | 
			
		||||
            } else {
 | 
			
		||||
                this.entries = CoreUtils.uniqueArray(this.entries
 | 
			
		||||
                    .concat(result.entries), 'id')
 | 
			
		||||
                    .sort((a, b) => b.created - a.created);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (this.onlyMyEntries) {
 | 
			
		||||
                const count = this.entries.filter((entry) => entry.userid == this.currentUserId).length;
 | 
			
		||||
                this.canLoadMoreUserEntries = result.totalentries > count;
 | 
			
		||||
                this.canLoadMore = this.canLoadMoreUserEntries;
 | 
			
		||||
                this.userPageLoaded++;
 | 
			
		||||
            } else {
 | 
			
		||||
                this.canLoadMoreEntries = result.totalentries > this.entries.length;
 | 
			
		||||
                this.canLoadMore = this.canLoadMoreEntries;
 | 
			
		||||
                this.pageLoaded++;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            await Promise.all(promises);
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            CoreDomUtils.showErrorModalDefault(error, 'addon.blog.errorloadentries', true);
 | 
			
		||||
            this.loadMoreError = true; // Set to prevent infinite calls with infinite-loading.
 | 
			
		||||
        } finally {
 | 
			
		||||
            this.loaded = true;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Toggle between showing only my entries or not.
 | 
			
		||||
     *
 | 
			
		||||
     * @param enabled If true, filter my entries. False otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    onlyMyEntriesToggleChanged(enabled: boolean): void {
 | 
			
		||||
        this.canLoadMore = enabled ? this.canLoadMoreUserEntries : this.canLoadMoreEntries;
 | 
			
		||||
 | 
			
		||||
        if (!enabled) {
 | 
			
		||||
            delete this.filter.userid;
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const count = this.entries.filter((entry) => entry.userid == this.currentUserId).length;
 | 
			
		||||
        this.userPageLoaded = Math.floor(count / AddonBlogProvider.ENTRIES_PER_PAGE);
 | 
			
		||||
        this.filter.userid = this.currentUserId;
 | 
			
		||||
 | 
			
		||||
        if (count == 0 && this.canLoadMoreUserEntries) {
 | 
			
		||||
            // First time but no entry loaded. Try to load some.
 | 
			
		||||
            this.loadMore();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Function to load more entries.
 | 
			
		||||
     *
 | 
			
		||||
     * @param infiniteComplete Infinite scroll complete function. Only used from core-infinite-loading.
 | 
			
		||||
     * @return Resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    loadMore(infiniteComplete?: () => void): Promise<void> {
 | 
			
		||||
        return this.fetchEntries().finally(() => {
 | 
			
		||||
            infiniteComplete && infiniteComplete();
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Refresh blog entries on PTR.
 | 
			
		||||
     *
 | 
			
		||||
     * @param refresher Refresher instance.
 | 
			
		||||
     */
 | 
			
		||||
    refresh(refresher?: CustomEvent<IonRefresher>): void {
 | 
			
		||||
        const promises = this.entries.map((entry) =>
 | 
			
		||||
            CoreComments.invalidateCommentsData('user', entry.userid, this.component, entry.id, 'format_blog'));
 | 
			
		||||
 | 
			
		||||
        promises.push(AddonBlog.invalidateEntries(this.filter));
 | 
			
		||||
 | 
			
		||||
        if (this.showMyEntriesToggle) {
 | 
			
		||||
            this.filter['userid'] = this.currentUserId;
 | 
			
		||||
            promises.push(AddonBlog.invalidateEntries(this.filter));
 | 
			
		||||
 | 
			
		||||
            if (!this.onlyMyEntries) {
 | 
			
		||||
                delete this.filter['userid'];
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        CoreUtils.allPromises(promises).finally(() => {
 | 
			
		||||
            this.fetchEntries(true).finally(() => {
 | 
			
		||||
                if (refresher) {
 | 
			
		||||
                    refresher?.detail.complete();
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Blog post with some calculated data.
 | 
			
		||||
 */
 | 
			
		||||
type AddonBlogPostFormatted = AddonBlogPost & {
 | 
			
		||||
    publishTranslated?: string; // Calculated in the app. Key of the string to translate the publish state of the post.
 | 
			
		||||
    user?: CoreUserProfile; // Calculated in the app. Data of the user that wrote the post.
 | 
			
		||||
    contextLevel?: string; // Calculated in the app. The context level of the entry.
 | 
			
		||||
    contextInstanceId?: number; // Calculated in the app. The context instance id.
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										204
									
								
								src/addons/blog/services/blog.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								src/addons/blog/services/blog.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,204 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { CoreSite, CoreSiteWSPreSets } from '@classes/site';
 | 
			
		||||
import { CorePushNotifications } from '@features/pushnotifications/services/pushnotifications';
 | 
			
		||||
import { CoreTagItem } from '@features/tag/services/tag';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreUtils } from '@services/utils/utils';
 | 
			
		||||
import { CoreStatusWithWarningsWSResponse, CoreWSExternalFile, CoreWSExternalWarning } from '@services/ws';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
 | 
			
		||||
const ROOT_CACHE_KEY = 'addonBlog:';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Service to handle blog entries.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonBlogProvider {
 | 
			
		||||
 | 
			
		||||
    static readonly ENTRIES_PER_PAGE = 10;
 | 
			
		||||
    static readonly COMPONENT = 'blog';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Returns whether or not the blog plugin is enabled for a certain site.
 | 
			
		||||
     *
 | 
			
		||||
     * This method is called quite often and thus should only perform a quick
 | 
			
		||||
     * check, we should not be calling WS from here.
 | 
			
		||||
     *
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved with true if enabled, resolved with false or rejected otherwise.
 | 
			
		||||
     */
 | 
			
		||||
    async isPluginEnabled(siteId?: string): Promise<boolean> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        return site.wsAvailable('core_blog_get_entries') &&site.canUseAdvancedFeature('enableblogs');
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get the cache key for the blog entries.
 | 
			
		||||
     *
 | 
			
		||||
     * @param filter Filter to apply on search.
 | 
			
		||||
     * @return Cache key.
 | 
			
		||||
     */
 | 
			
		||||
    getEntriesCacheKey(filter: AddonBlogFilter = {}): string {
 | 
			
		||||
        return ROOT_CACHE_KEY + CoreUtils.sortAndStringify(filter);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Get blog entries.
 | 
			
		||||
     *
 | 
			
		||||
     * @param filter Filter to apply on search.
 | 
			
		||||
     * @param page Page of the blog entries to fetch.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise to be resolved when the entries are retrieved.
 | 
			
		||||
     */
 | 
			
		||||
    async getEntries(filter: AddonBlogFilter = {}, page: number = 0, siteId?: string): Promise<CoreBlogGetEntriesWSResponse> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const data: CoreBlogGetEntriesWSParams = {
 | 
			
		||||
            filters: CoreUtils.objectToArrayOfObjects(filter, 'name', 'value'),
 | 
			
		||||
            page: page,
 | 
			
		||||
            perpage: AddonBlogProvider.ENTRIES_PER_PAGE,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const preSets: CoreSiteWSPreSets = {
 | 
			
		||||
            cacheKey: this.getEntriesCacheKey(filter),
 | 
			
		||||
            updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return site.read('core_blog_get_entries', data, preSets);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Invalidate blog entries WS call.
 | 
			
		||||
     *
 | 
			
		||||
     * @param filter Filter to apply on search
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise resolved when data is invalidated.
 | 
			
		||||
     */
 | 
			
		||||
    async invalidateEntries(filter: AddonBlogFilter = {}, siteId?: string): Promise<void> {
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        await site.invalidateWsCacheForKey(this.getEntriesCacheKey(filter));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Trigger the blog_entries_viewed event.
 | 
			
		||||
     *
 | 
			
		||||
     * @param filter Filter to apply on search.
 | 
			
		||||
     * @param siteId Site ID. If not defined, current site.
 | 
			
		||||
     * @return Promise to be resolved when done.
 | 
			
		||||
     */
 | 
			
		||||
    async logView(filter: AddonBlogFilter = {}, siteId?: string): Promise<CoreStatusWithWarningsWSResponse> {
 | 
			
		||||
        CorePushNotifications.logViewListEvent('blog', 'core_blog_view_entries', filter, siteId);
 | 
			
		||||
 | 
			
		||||
        const site = await CoreSites.getSite(siteId);
 | 
			
		||||
 | 
			
		||||
        const data: AddonBlogViewEntriesWSParams = {
 | 
			
		||||
            filters: CoreUtils.objectToArrayOfObjects(filter, 'name', 'value'),
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        return site.write('core_blog_view_entries', data);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonBlog = makeSingleton(AddonBlogProvider);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of core_blog_get_entries WS.
 | 
			
		||||
 */
 | 
			
		||||
type CoreBlogGetEntriesWSParams = {
 | 
			
		||||
    filters?: { // Parameters to filter blog listings.
 | 
			
		||||
        name: string; // The expected keys (value format) are:
 | 
			
		||||
        // tag      PARAM_NOTAGS blog tag
 | 
			
		||||
        // tagid    PARAM_INT    blog tag id
 | 
			
		||||
        // userid   PARAM_INT    blog author (userid)
 | 
			
		||||
        // cmid    PARAM_INT    course module id
 | 
			
		||||
        // entryid  PARAM_INT    entry id
 | 
			
		||||
        // groupid  PARAM_INT    group id
 | 
			
		||||
        // courseid PARAM_INT    course id
 | 
			
		||||
        // search   PARAM_RAW    search term.
 | 
			
		||||
        value: string; // The value of the filter.
 | 
			
		||||
    }[];
 | 
			
		||||
    page?: number; // The blog page to return.
 | 
			
		||||
    perpage?: number; // The number of posts to return per page.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by core_blog_get_entries WS.
 | 
			
		||||
 */
 | 
			
		||||
export type CoreBlogGetEntriesWSResponse = {
 | 
			
		||||
    entries: AddonBlogPost[];
 | 
			
		||||
    totalentries: number; // The total number of entries found.
 | 
			
		||||
    warnings?: CoreWSExternalWarning[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Data returned by blog's post_exporter.
 | 
			
		||||
 */
 | 
			
		||||
export type AddonBlogPost = {
 | 
			
		||||
    id: number; // Post/entry id.
 | 
			
		||||
    module: string; // Where it was published the post (blog, blog_external...).
 | 
			
		||||
    userid: number; // Post author.
 | 
			
		||||
    courseid: number; // Course where the post was created.
 | 
			
		||||
    groupid: number; // Group post was created for.
 | 
			
		||||
    moduleid: number; // Module id where the post was created (not used anymore).
 | 
			
		||||
    coursemoduleid: number; // Course module id where the post was created.
 | 
			
		||||
    subject: string; // Post subject.
 | 
			
		||||
    summary: string; // Post summary.
 | 
			
		||||
    summaryformat?: number; // Summary format (1 = HTML, 0 = MOODLE, 2 = PLAIN or 4 = MARKDOWN).
 | 
			
		||||
    content: string; // Post content.
 | 
			
		||||
    uniquehash: string; // Post unique hash.
 | 
			
		||||
    rating: number; // Post rating.
 | 
			
		||||
    format: number; // Post content format.
 | 
			
		||||
    attachment: string; // Post atachment.
 | 
			
		||||
    publishstate: string; // Post publish state.
 | 
			
		||||
    lastmodified: number; // When it was last modified.
 | 
			
		||||
    created: number; // When it was created.
 | 
			
		||||
    usermodified: number; // User that updated the post.
 | 
			
		||||
    summaryfiles: CoreWSExternalFile[]; // Summaryfiles.
 | 
			
		||||
    attachmentfiles?: CoreWSExternalFile[]; // Attachmentfiles.
 | 
			
		||||
    tags?: CoreTagItem[]; // @since 3.7. Tags.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Params of core_blog_view_entries WS.
 | 
			
		||||
 */
 | 
			
		||||
type AddonBlogViewEntriesWSParams = {
 | 
			
		||||
    filters?: { // Parameters used in the filter of view_entries.
 | 
			
		||||
        name: string; // The expected keys (value format) are:
 | 
			
		||||
        // tag      PARAM_NOTAGS blog tag
 | 
			
		||||
        // tagid    PARAM_INT    blog tag id
 | 
			
		||||
        // userid   PARAM_INT    blog author (userid)
 | 
			
		||||
        // cmid     PARAM_INT    course module id
 | 
			
		||||
        // entryid  PARAM_INT    entry id
 | 
			
		||||
        // groupid  PARAM_INT    group id
 | 
			
		||||
        // courseid PARAM_INT    course id
 | 
			
		||||
        // search   PARAM_RAW    search term.
 | 
			
		||||
        value: string; // The value of the filter.
 | 
			
		||||
    }[];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
export type AddonBlogFilter = {
 | 
			
		||||
    tag?: string;      // Blog tag
 | 
			
		||||
    tagid?: number;    // Blog tag id
 | 
			
		||||
    userid?: number;   // Blog author (userid)
 | 
			
		||||
    cmid?: number;     // Course module id
 | 
			
		||||
    entryid?: number;  // Entry id
 | 
			
		||||
    groupid?: number;  // Group id
 | 
			
		||||
    courseid?: number; // Course id
 | 
			
		||||
    search?: string;   // Search term.
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										109
									
								
								src/addons/blog/services/handlers/course-option.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								src/addons/blog/services/handlers/course-option.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,109 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { CoreCourse } from '@features/course/services/course';
 | 
			
		||||
import { CoreCourseHelper } from '@features/course/services/course-helper';
 | 
			
		||||
import {
 | 
			
		||||
    CoreCourseAccess,
 | 
			
		||||
    CoreCourseOptionsHandler,
 | 
			
		||||
    CoreCourseOptionsHandlerData,
 | 
			
		||||
} from '@features/course/services/course-options-delegate';
 | 
			
		||||
import { CoreCourseUserAdminOrNavOptionIndexed } from '@features/courses/services/courses';
 | 
			
		||||
import { CoreEnrolledCourseDataWithExtraInfoAndOptions } from '@features/courses/services/courses-helper';
 | 
			
		||||
import { CoreFilepool } from '@services/filepool';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreWSExternalFile } from '@services/ws';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonBlog } from '../blog';
 | 
			
		||||
import { AddonBlogMainMenuHandlerService } from './mainmenu';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Course nav handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonBlogCourseOptionHandlerService implements CoreCourseOptionsHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonBlog';
 | 
			
		||||
    priority = 100;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    invalidateEnabledForCourse(courseId: number): Promise<void> {
 | 
			
		||||
        return CoreCourse.invalidateCourseBlocks(courseId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonBlog.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabledForCourse(
 | 
			
		||||
        courseId: number,
 | 
			
		||||
        accessData: CoreCourseAccess,
 | 
			
		||||
        navOptions?: CoreCourseUserAdminOrNavOptionIndexed,
 | 
			
		||||
    ): Promise<boolean> {
 | 
			
		||||
        const enabled = await CoreCourseHelper.hasABlockNamed(courseId, 'blog_menu');
 | 
			
		||||
 | 
			
		||||
        if (enabled && navOptions && typeof navOptions.blogs != 'undefined') {
 | 
			
		||||
            return navOptions.blogs;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return enabled;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getDisplayData(): CoreCourseOptionsHandlerData | Promise<CoreCourseOptionsHandlerData> {
 | 
			
		||||
        return {
 | 
			
		||||
            title: 'addon.blog.blog',
 | 
			
		||||
            class: 'addon-blog-handler',
 | 
			
		||||
            page: AddonBlogMainMenuHandlerService.PAGE_NAME,
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async prefetch(course: CoreEnrolledCourseDataWithExtraInfoAndOptions): Promise<void> {
 | 
			
		||||
        const siteId = CoreSites.getCurrentSiteId();
 | 
			
		||||
 | 
			
		||||
        const result = await AddonBlog.getEntries({ courseid: course.id });
 | 
			
		||||
 | 
			
		||||
        await Promise.all(result.entries.map(async (entry) => {
 | 
			
		||||
            let files: CoreWSExternalFile[] = [];
 | 
			
		||||
 | 
			
		||||
            if (entry.attachmentfiles && entry.attachmentfiles.length) {
 | 
			
		||||
                files = entry.attachmentfiles;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (entry.summaryfiles && entry.summaryfiles.length) {
 | 
			
		||||
                files = files.concat(entry.summaryfiles);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (files.length > 0) {
 | 
			
		||||
                await CoreFilepool.addFilesToQueue(siteId, files, entry.module, entry.id);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonBlogCourseOptionHandler = makeSingleton(AddonBlogCourseOptionHandlerService);
 | 
			
		||||
							
								
								
									
										61
									
								
								src/addons/blog/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/addons/blog/services/handlers/index-link.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,61 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { Params } from '@angular/router';
 | 
			
		||||
import { CoreContentLinksHandlerBase } from '@features/contentlinks/classes/base-handler';
 | 
			
		||||
import { CoreContentLinksAction } from '@features/contentlinks/services/contentlinks-delegate';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonBlog } from '../blog';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to treat links to blog page.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonBlogIndexLinkHandlerService extends CoreContentLinksHandlerBase {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonBlogIndexLinkHandler';
 | 
			
		||||
    featureName = 'CoreUserDelegate_AddonBlog:blogs';
 | 
			
		||||
    pattern = /\/blog\/index\.php/;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getActions(siteIds: string[], url: string, params: Record<string, string>): CoreContentLinksAction[] {
 | 
			
		||||
        const pageParams: Params = {};
 | 
			
		||||
 | 
			
		||||
        params.userid ? pageParams['userId'] = parseInt(params.userid, 10) : null;
 | 
			
		||||
        params.modid ? pageParams['cmId'] = parseInt(params.modid, 10) : null;
 | 
			
		||||
        params.courseid ? pageParams['courseId'] = parseInt(params.courseid, 10) : null;
 | 
			
		||||
        params.entryid ? pageParams['entryId'] = parseInt(params.entryid, 10) : null;
 | 
			
		||||
        params.groupid ? pageParams['groupId'] = parseInt(params.groupid, 10) : null;
 | 
			
		||||
        params.tagid ? pageParams['tagId'] = parseInt(params.tagid, 10) : null;
 | 
			
		||||
 | 
			
		||||
        return [{
 | 
			
		||||
            action: (siteId: string): void => {
 | 
			
		||||
                CoreNavigator.navigateToSitePath('/blog', { params: pageParams, siteId });
 | 
			
		||||
            },
 | 
			
		||||
        }];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(siteId: string): Promise<boolean> {
 | 
			
		||||
        return AddonBlog.isPluginEnabled(siteId);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonBlogIndexLinkHandler = makeSingleton(AddonBlogIndexLinkHandlerService);
 | 
			
		||||
							
								
								
									
										51
									
								
								src/addons/blog/services/handlers/mainmenu.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								src/addons/blog/services/handlers/mainmenu.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,51 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { CoreMainMenuHandler, CoreMainMenuHandlerData } from '@features/mainmenu/services/mainmenu-delegate';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonBlog } from '../blog';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to inject an option into main menu.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonBlogMainMenuHandlerService implements CoreMainMenuHandler {
 | 
			
		||||
 | 
			
		||||
    static readonly PAGE_NAME = 'blog';
 | 
			
		||||
 | 
			
		||||
    name = 'AddonBlog';
 | 
			
		||||
    priority = 450;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonBlog.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getDisplayData(): CoreMainMenuHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            icon: 'far-newspaper',
 | 
			
		||||
            title: 'addon.blog.siteblogheading',
 | 
			
		||||
            page: AddonBlogMainMenuHandlerService.PAGE_NAME,
 | 
			
		||||
            class: 'addon-blog-handler',
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonBlogMainMenuHandler = makeSingleton(AddonBlogMainMenuHandlerService);
 | 
			
		||||
							
								
								
									
										53
									
								
								src/addons/blog/services/handlers/tag-area.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								src/addons/blog/services/handlers/tag-area.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,53 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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, Type } from '@angular/core';
 | 
			
		||||
import { CoreTagFeedComponent } from '@features/tag/components/feed/feed';
 | 
			
		||||
import { CoreTagAreaHandler } from '@features/tag/services/tag-area-delegate';
 | 
			
		||||
import { CoreTagFeedElement, CoreTagHelper } from '@features/tag/services/tag-helper';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonBlog } from '../blog';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Handler to support tags.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonBlogTagAreaHandlerService implements CoreTagAreaHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonBlogTagAreaHandler';
 | 
			
		||||
    type = 'core/post';
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonBlog.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async parseContent(content: string): Promise<CoreTagFeedElement[]> {
 | 
			
		||||
        return CoreTagHelper.parseFeedContent(content);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getComponent(): Type<unknown> | Promise<Type<unknown>> {
 | 
			
		||||
        return CoreTagFeedComponent;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonBlogTagAreaHandler = makeSingleton(AddonBlogTagAreaHandlerService);
 | 
			
		||||
							
								
								
									
										64
									
								
								src/addons/blog/services/handlers/user.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/addons/blog/services/handlers/user.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,64 @@
 | 
			
		||||
// (C) Copyright 2015 Moodle Pty Ltd.
 | 
			
		||||
//
 | 
			
		||||
// 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 { CoreUserProfileHandler, CoreUserProfileHandlerData, CoreUserDelegateService } from '@features/user/services/user-delegate';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { makeSingleton } from '@singletons';
 | 
			
		||||
import { AddonBlog } from '../blog';
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Profile item handler.
 | 
			
		||||
 */
 | 
			
		||||
@Injectable({ providedIn: 'root' })
 | 
			
		||||
export class AddonBlogUserHandlerService implements CoreUserProfileHandler {
 | 
			
		||||
 | 
			
		||||
    name = 'AddonBlog:blogs';
 | 
			
		||||
    priority = 300;
 | 
			
		||||
    type = CoreUserDelegateService.TYPE_NEW_PAGE;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    isEnabled(): Promise<boolean> {
 | 
			
		||||
        return AddonBlog.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    async isEnabledForUser(): Promise<boolean> {
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @inheritdoc
 | 
			
		||||
     */
 | 
			
		||||
    getDisplayData(): CoreUserProfileHandlerData {
 | 
			
		||||
        return {
 | 
			
		||||
            icon: 'far-newspaper',
 | 
			
		||||
            title: 'addon.blog.blogentries',
 | 
			
		||||
            class: 'addon-blog-handler',
 | 
			
		||||
            action: (event, user, courseId): void => {
 | 
			
		||||
                event.preventDefault();
 | 
			
		||||
                event.stopPropagation();
 | 
			
		||||
                CoreNavigator.navigateToSitePath('/blog', {
 | 
			
		||||
                    params: { courseId, userId: user.id },
 | 
			
		||||
                });
 | 
			
		||||
            },
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
export const AddonBlogUserHandler = makeSingleton(AddonBlogUserHandlerService);
 | 
			
		||||
@ -25,7 +25,6 @@ export class AddonCalendarMainMenuHandlerService implements CoreMainMenuHandler
 | 
			
		||||
 | 
			
		||||
    static readonly PAGE_NAME = 'calendar';
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    name = 'AddonCalendar';
 | 
			
		||||
    priority = 900;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@
 | 
			
		||||
            [priority]="800" [content]="'core.moduleintro' | translate" (action)="expandDescription()" [iconAction]="'arrow-forward'">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="blog"
 | 
			
		||||
            [priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'fa-newspaper-o'" (action)="gotoBlog()">
 | 
			
		||||
            [priority]="750" content="{{'addon.blog.blog' | translate}}" [iconAction]="'far-newspaper'" (action)="gotoBlog()">
 | 
			
		||||
        </core-context-menu-item>
 | 
			
		||||
        <core-context-menu-item *ngIf="discussions.loaded && !(hasOffline || hasOfflineRatings) && isOnline"
 | 
			
		||||
            [priority]="700" [content]="'addon.mod_forum.refreshdiscussions' | translate" [iconAction]="refreshIcon" [closeOnClick]="false"
 | 
			
		||||
 | 
			
		||||
@ -77,6 +77,10 @@ export class CoreInfiniteLoadingComponent implements OnChanges {
 | 
			
		||||
 | 
			
		||||
        // Calculate distance from edge.
 | 
			
		||||
        const content = this.element.nativeElement.closest('ion-content') as IonContent;
 | 
			
		||||
        if (!content) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        const scrollElement = await content.getScrollElement();
 | 
			
		||||
 | 
			
		||||
        const infiniteHeight = this.element.nativeElement.getBoundingClientRect().height;
 | 
			
		||||
 | 
			
		||||
@ -13,9 +13,13 @@
 | 
			
		||||
// limitations under the License.
 | 
			
		||||
 | 
			
		||||
import { CoreConstants } from '@/core/constants';
 | 
			
		||||
import { AddonBlog } from '@addons/blog/services/blog';
 | 
			
		||||
import { AddonBlogMainMenuHandlerService } from '@addons/blog/services/handlers/mainmenu';
 | 
			
		||||
import { OnInit, OnDestroy, Input, Output, EventEmitter, Component, Optional, Inject } from '@angular/core';
 | 
			
		||||
import { Params } from '@angular/router';
 | 
			
		||||
import { IonRefresher } from '@ionic/angular';
 | 
			
		||||
import { CoreApp } from '@services/app';
 | 
			
		||||
import { CoreNavigator } from '@services/navigator';
 | 
			
		||||
import { CoreSites } from '@services/sites';
 | 
			
		||||
import { CoreDomUtils } from '@services/utils/dom';
 | 
			
		||||
 | 
			
		||||
@ -90,7 +94,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
        this.componentId = this.module?.id;
 | 
			
		||||
        this.externalUrl = this.module?.url;
 | 
			
		||||
        this.courseId = this.courseId || this.module?.course;
 | 
			
		||||
        // @todo this.blog = await this.blogProvider.isPluginEnabled();
 | 
			
		||||
        this.blog = await AddonBlog.isPluginEnabled();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@ -223,8 +227,9 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
 | 
			
		||||
     * Go to blog posts.
 | 
			
		||||
     */
 | 
			
		||||
    async gotoBlog(): Promise<void> {
 | 
			
		||||
        // const params: Params = { cmId: this.module?.id };
 | 
			
		||||
        // @todo return CoreNavigator.navigateToSitePath('AddonBlogEntriesPage', { params });
 | 
			
		||||
        const params: Params = { cmId: this.module?.id };
 | 
			
		||||
 | 
			
		||||
        CoreNavigator.navigateToSitePath(AddonBlogMainMenuHandlerService.PAGE_NAME, { params });
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user