MOBILE-4207 forum: Implement forum activity search
parent
cd85155953
commit
5873da0e0c
|
@ -1,5 +1,8 @@
|
|||
<!-- Buttons to add to the header. -->
|
||||
<core-navbar-buttons slot="end">
|
||||
<ion-button fill="clear" (click)="openSearch()" [attr.aria-label]="'core.search' | translate">
|
||||
<ion-icon name="fas-magnifying-glass" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
<ion-button fill="clear" (click)="openModuleSummary()" aria-haspopup="true" [attr.aria-label]="'core.info' | translate">
|
||||
<ion-icon name="fas-circle-info" slot="icon-only" aria-hidden="true"></ion-icon>
|
||||
</ion-button>
|
||||
|
|
|
@ -57,6 +57,8 @@ import { AddonModForumDiscussionItem, AddonModForumDiscussionsSource } from '../
|
|||
import { CoreListItemsManager } from '@classes/items-management/list-items-manager';
|
||||
import { CoreRoutedItemsManagerSourcesTracker } from '@classes/items-management/routed-items-manager-sources-tracker';
|
||||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { FORUM_SEARCH_PAGE_NAME } from '@addons/mod/forum/forum.module';
|
||||
|
||||
/**
|
||||
* Component that displays a forum entry page.
|
||||
|
@ -332,6 +334,22 @@ export class AddonModForumIndexComponent extends CoreCourseModuleMainActivityCom
|
|||
this.discussions?.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Open search page.
|
||||
*/
|
||||
async openSearch(): Promise<void> {
|
||||
if (!this.forum) {
|
||||
return;
|
||||
}
|
||||
|
||||
await CoreNavigator.navigateToSitePath(FORUM_SEARCH_PAGE_NAME, {
|
||||
params: {
|
||||
courseId: this.courseId,
|
||||
forumId: this.forum.id,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
<ion-back-button [text]="'core.back' | translate"></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>
|
||||
<h1>{{ 'addon.block_searchforums.pluginname' | translate }}</h1>
|
||||
<h1 *ngIf="forum">{{ forum.name }}</h1>
|
||||
<h1 *ngIf="!forum">{{ 'addon.block_searchforums.pluginname' | translate }}</h1>
|
||||
</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<core-user-menu-button></core-user-menu-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content class="limited-width">
|
||||
|
|
|
@ -12,18 +12,24 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { AddonModForum, AddonModForumData } from '@addons/mod/forum/services/forum';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { CorePromisedValue } from '@classes/promised-value';
|
||||
import { CoreContentLinksHelper } from '@features/contentlinks/services/contentlinks-helper';
|
||||
import { CoreCourse } from '@features/course/services/course';
|
||||
import { CoreSearchGlobalSearchResultsSource } from '@features/search/classes/global-search-results-source';
|
||||
import {
|
||||
CoreSearchGlobalSearch,
|
||||
CoreSearchGlobalSearchFilters,
|
||||
CoreSearchGlobalSearchResult,
|
||||
} from '@features/search/services/global-search';
|
||||
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
|
||||
import { CoreNavigator } from '@services/navigator';
|
||||
import { CoreSites } from '@services/sites';
|
||||
import { CoreDomUtils } from '@services/utils/dom';
|
||||
import { CoreUrlUtils } from '@services/utils/url';
|
||||
import { CoreUtils } from '@services/utils/utils';
|
||||
import { Translate } from '@singletons';
|
||||
|
||||
@Component({
|
||||
selector: 'page-addon-mod-forum-search',
|
||||
|
@ -35,16 +41,20 @@ export class AddonModForumSearchPage implements OnInit {
|
|||
loadMoreError: string | null = null;
|
||||
searchBanner: string | null = null;
|
||||
resultsSource = new CoreSearchGlobalSearchResultsSource('', {});
|
||||
forum?: AddonModForumData;
|
||||
searchAreaId?: string;
|
||||
|
||||
private ready = new CorePromisedValue<void>();
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*/
|
||||
ngOnInit(): void {
|
||||
async ngOnInit(): Promise<void> {
|
||||
try {
|
||||
const site = CoreSites.getRequiredCurrentSite();
|
||||
const searchBanner = site.config?.searchbanner?.trim() ?? '';
|
||||
const courseId = CoreNavigator.getRequiredRouteNumberParam('courseId');
|
||||
const forumId = CoreNavigator.getRouteNumberParam('forumId');
|
||||
const filters: CoreSearchGlobalSearchFilters = {
|
||||
courseIds: [courseId],
|
||||
};
|
||||
|
@ -53,10 +63,24 @@ export class AddonModForumSearchPage implements OnInit {
|
|||
this.searchBanner = searchBanner;
|
||||
}
|
||||
|
||||
filters.searchAreaIds = ['mod_forum-activity', 'mod_forum-post'];
|
||||
this.searchAreaId = `AddonModForumSearch-${courseId}`;
|
||||
if (forumId) {
|
||||
this.forum = await AddonModForum.getForumById(courseId, forumId);
|
||||
const module = await CoreCourse.getModule(this.forum.cmid, courseId);
|
||||
|
||||
filters.searchAreaIds = ['mod_forum-post'];
|
||||
|
||||
if (module.contextid) {
|
||||
filters.contextIds = [module.contextid];
|
||||
}
|
||||
|
||||
this.searchAreaId = `AddonModForumSearch-${courseId}-${this.forum.id}`;
|
||||
} else {
|
||||
filters.searchAreaIds = ['mod_forum-activity', 'mod_forum-post'];
|
||||
this.searchAreaId = `AddonModForumSearch-${courseId}`;
|
||||
}
|
||||
|
||||
this.resultsSource.setFilters(filters);
|
||||
this.ready.resolve();
|
||||
} catch (error) {
|
||||
CoreDomUtils.showErrorModal(error);
|
||||
CoreNavigator.back();
|
||||
|
@ -71,6 +95,8 @@ export class AddonModForumSearchPage implements OnInit {
|
|||
* @param query Search query.
|
||||
*/
|
||||
async search(query: string): Promise<void> {
|
||||
await this.ready;
|
||||
|
||||
this.resultsSource.setQuery(query);
|
||||
|
||||
if (this.resultsSource.hasEmptyQuery()) {
|
||||
|
@ -82,6 +108,19 @@ export class AddonModForumSearchPage implements OnInit {
|
|||
await CoreUtils.ignoreErrors(
|
||||
CoreSearchGlobalSearch.logViewResults(this.resultsSource.getQuery(), this.resultsSource.getFilters()),
|
||||
);
|
||||
|
||||
CoreAnalytics.logEvent({
|
||||
type: CoreAnalyticsEventType.VIEW_ITEM_LIST,
|
||||
ws: 'core_search_view_results',
|
||||
name: Translate.instant('core.search.globalsearch'),
|
||||
data: {
|
||||
query,
|
||||
filters: JSON.stringify(this.resultsSource.getFilters()),
|
||||
},
|
||||
url: CoreUrlUtils.addParamsToUrl('/search/index.php', {
|
||||
q: query,
|
||||
}),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -90,6 +129,9 @@ export class AddonModForumSearchPage implements OnInit {
|
|||
*/
|
||||
clearSearch(): void {
|
||||
this.loadMoreError = null;
|
||||
|
||||
this.resultsSource.setQuery('');
|
||||
this.resultsSource.reset();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -28,6 +28,8 @@ Feature: Test Forum Search
|
|||
| forum2 | Initial discussion 2 | Initial discussion 2 | Initial discussion message 2 |
|
||||
| forum3 | Initial discussion 3 | Initial discussion 3 | Initial discussion message 3 |
|
||||
|
||||
# TODO test single forum search (lacking generators for post search results)
|
||||
|
||||
Scenario: Search in side block
|
||||
Given global search expects the query "message" and will return:
|
||||
| type | idnumber |
|
||||
|
|
|
@ -44,7 +44,7 @@ export class CoreSearchGlobalSearchResultComponent implements OnChanges {
|
|||
* @returns Rendered context.
|
||||
*/
|
||||
private computeRenderedContext(): CoreSearchGlobalSearchResultContext | null {
|
||||
const context = this.result.context ?? {};
|
||||
const context = { ...this.result.context } ?? {};
|
||||
|
||||
if (this.showCourse === false) {
|
||||
delete context.courseName;
|
||||
|
|
|
@ -80,6 +80,7 @@ export interface CoreSearchGlobalSearchFilters {
|
|||
searchAreaCategoryIds?: string[];
|
||||
searchAreaIds?: string[];
|
||||
courseIds?: number[];
|
||||
contextIds?: number[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -130,7 +131,7 @@ export class CoreSearchGlobalSearchService {
|
|||
const params: CoreSearchGetResultsWSParams = {
|
||||
query,
|
||||
page,
|
||||
filters: await this.prepareWSFilters(filters),
|
||||
filters: await this.prepareAdvancedWSFilters(filters),
|
||||
};
|
||||
const preSets = CoreSites.getReadingStrategyPreSets(CoreSitesReadingStrategy.PREFER_NETWORK);
|
||||
|
||||
|
@ -158,7 +159,7 @@ export class CoreSearchGlobalSearchService {
|
|||
const site = CoreSites.getRequiredCurrentSite();
|
||||
const params: CoreSearchGetTopResultsWSParams = {
|
||||
query,
|
||||
filters: await this.prepareWSFilters(filters),
|
||||
filters: await this.prepareAdvancedWSFilters(filters),
|
||||
};
|
||||
const preSets = CoreSites.getReadingStrategyPreSets(CoreSitesReadingStrategy.PREFER_NETWORK);
|
||||
|
||||
|
@ -210,7 +211,7 @@ export class CoreSearchGlobalSearchService {
|
|||
const site = CoreSites.getRequiredCurrentSite();
|
||||
const params: CoreSearchViewResultsWSParams = {
|
||||
query,
|
||||
filters: await this.prepareWSFilters(filters),
|
||||
filters: await this.prepareBasicWSFilters(filters),
|
||||
};
|
||||
|
||||
await site.write<CoreSearchViewResultsWSResponse>('core_search_view_results', params);
|
||||
|
@ -273,17 +274,18 @@ export class CoreSearchGlobalSearchService {
|
|||
*/
|
||||
protected filtersYieldEmptyResults(filters: CoreSearchGlobalSearchFilters): boolean {
|
||||
return filters.courseIds?.length === 0
|
||||
|| filters.contextIds?.length === 0
|
||||
|| filters.searchAreaIds?.length === 0
|
||||
|| filters.searchAreaCategoryIds?.length === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare search filters before sending to WS.
|
||||
* Prepare basic search filters before sending to WS.
|
||||
*
|
||||
* @param filters App filters.
|
||||
* @returns WS filters.
|
||||
* @returns Basic WS filters.
|
||||
*/
|
||||
protected async prepareWSFilters(filters: CoreSearchGlobalSearchFilters): Promise<CoreSearchBasicWSFilters> {
|
||||
protected async prepareBasicWSFilters(filters: CoreSearchGlobalSearchFilters): Promise<CoreSearchBasicWSFilters> {
|
||||
const wsFilters: CoreSearchBasicWSFilters = {};
|
||||
|
||||
if (filters.courseIds) {
|
||||
|
@ -311,6 +313,22 @@ export class CoreSearchGlobalSearchService {
|
|||
return wsFilters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare advanced search filters before sending to WS.
|
||||
*
|
||||
* @param filters App filters.
|
||||
* @returns Advanced WS filters.
|
||||
*/
|
||||
protected async prepareAdvancedWSFilters(filters: CoreSearchGlobalSearchFilters): Promise<CoreSearchAdvancedWSFilters> {
|
||||
const wsFilters: CoreSearchAdvancedWSFilters = await this.prepareBasicWSFilters(filters);
|
||||
|
||||
if (filters.contextIds) {
|
||||
wsFilters.contextids = filters.contextIds;
|
||||
}
|
||||
|
||||
return wsFilters;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export const CoreSearchGlobalSearch = makeSingleton(CoreSearchGlobalSearchService);
|
||||
|
|
Loading…
Reference in New Issue