# Knowledge Base Display Hooks

https://dev.wisecp.com/es/knowledge-base-display-hooks

The nine hooks over the knowledge base a visitor sees: the body, the tree, the scope, the popular box, search, and four observation points.

## Overview

Everything a visitor sees passes through these hooks: the article body, the category tree, the popular box and the search results. All four are **passed by link**, so you make your change by writing over the incoming value rather than returning it.

Beside them sit four observation hooks: an article read, a category opened, a vote and a search. They change nothing; they report that something happened.

## Reference

### Following an article being read

actionknowledgebase.article.viewed

`website/knowledgebase` on every opening

Runs when an article is viewed. The visitor may well not be logged in.

Parameters 2

$idintThe id of the article viewed.

$articlearrayThe article data, with the view counter **already increased**.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:knowledgebase.article.viewed', 10, function ($id, $article) {
    // The visitor may not be logged in: build no assumption about identity.
    Acme::trackRead($id);
});
```

### Following an article vote

actionknowledgebase.article.voted

`website/knowledgebase` one of two values

Runs when a visitor marks an article useful or not.

Parameters 2

$idintThe id of the article voted on.

$typestringWhich way: `useful` or `useless`.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:knowledgebase.article.voted', 10, function ($id, $type) {
    // Collect the unhelpful votes for the writing team.
    if ($type === 'useless') Acme::flagForReview($id);
});
```

### Following a category being opened

actionknowledgebase.category.viewed

`website/knowledgebase` in the chosen language

Runs when a category page is viewed.

Parameters 2

$catIdintThe id of the category viewed.

$catarrayThe category record **in the chosen language**: title, link and search headings.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:knowledgebase.category.viewed', 10, function ($catId, $cat) {
    Acme::trackCategory($catId);
});
```

### Following a search

actionknowledgebase.searched

`website/knowledgebase` after the filter

Runs when a visitor searches. The result **may be empty**, and searches that find nothing are the best list of articles still to write.

Parameters 3

$querystringThe cleaned search term; it never arrives empty.

$resultsarrayThe final results, **after the filter ran**. An empty array means nothing was found.

$langstringThe language the search ran in.

Return 1

voidThe return is ignored. To change the results use the search results filter, not this hook.

Listener PHP

```php
Hook::add('action:knowledgebase.searched', 10, function ($query, $results, $lang) {
    // Searches that find nothing point at the article still to write.
    if (!$results) Acme::noteGap($query, $lang);
});
```

### Changing the article body

filterknowledgebase.article_content

`website/knowledgebase` passed by link

Runs before the article body reaches the screen. Resolve your own shortcodes here, and drop in values like a version number.

Parameters 2

$contentstringby linkThe raw body of the article. What you write over it is the final body.

$ctxarrayby linkContext: the article id, the active language and its category.

Return 1

voidThe return is ignored; you write over the body. The body is raw markup and goes straight to the screen: clean any user data you put into it yourself.

Listener PHP

```php
Hook::add('filter:knowledgebase.article_content', 10, function (&$content, &$ctx) {
    // Resolve a shortcode of your own.
    $content = str_replace('[version]', Acme::currentVersion(), $content);
});
```

### Limiting which categories are visible

filterknowledgebase.category_scope

`website/knowledgebase` empty means no limit

Runs while article queries are built. This is how you narrow the knowledge base to an audience: one set for resellers, another for end customers.

Parameters 2

$scopearrayby linkThe category ids the queries will be held to. It **always arrives empty**, and **leaving it empty means no limit**: touch nothing and the whole knowledge base shows.

$ctxarrayby linkContext: which page and which language. It is information only; changing it affects nothing.

Return 1

voidThe return is ignored; you write ids into the list.

Listener PHP

```php
Hook::add('filter:knowledgebase.category_scope', 10, function (&$scope, &$ctx) {
    // Leaving it empty means "no limit": fill it to narrow the view.
    if (!Acme::isReseller()) $scope = Acme::publicCategories();
});
```

### Changing the category tree

filterknowledgebase.category_tree

`website/knowledgebase` passed by link

Runs once the category tree beside the content is prepared.

Parameters 2

$treearrayby linkThe top-level nodes. What you write over them is the final tree.

$ctxarrayby linkContext: which page and which language.

Return 1

voidThe return is ignored; you write over the tree.

Listener PHP

```php
Hook::add('filter:knowledgebase.category_tree', 10, function (&$tree, &$ctx) {
    // Hide the internal branch from visitors.
    $tree = array_values(array_filter($tree, fn ($n) => ($n['id'] ?? 0) !== Acme::INTERNAL_CAT));
});
```

### Changing the popular article list

filterknowledgebase.popular_articles

`website/knowledgebase` passed by link

Runs while the popular article box beside the content is filled.

Parameters 2

$articlesarrayby linkThe articles to list: titles, links and view counts.

$ctxarrayby linkContext: which page, which language and **how many were asked for**. Mind that limit when adding: the box does not stretch.

Return 1

voidThe return is ignored; you write over the list.

Listener PHP

```php
Hook::add('filter:knowledgebase.popular_articles', 10, function (&$articles, &$ctx) {
    // The limit arrives in the context: the box does not stretch.
    array_unshift($articles, Acme::featuredArticle());
    $articles = array_slice($articles, 0, (int) ($ctx['limit'] ?? 6));
});
```

### Changing the search results

filterknowledgebase.search_results

`website/knowledgebase` passed by link

Runs before the search results reach the screen. Add results from a source of your own here, or change the order.

Parameters 2

$resultsarrayby linkThe raw result rows: title, link, category name and view count.

$ctxarrayby linkContext: the search text and the language.

Return 1

voidThe return is ignored; you write over the list. Your change carries into the search event as well: that hook runs **after** you and sees the adjusted list.

Listener PHP

```php
Hook::add('filter:knowledgebase.search_results', 10, function (&$results, &$ctx) {
    // Add results from a source of your own.
    foreach (Acme::search($ctx['query'] ?? '', $ctx['lang'] ?? '') as $row) $results[] = $row;
});
```

## Pitfalls

> **An empty scope is no limit, not a tight one**
> 
> The scope list **always arrives empty**, and leaving it that way means "show the whole knowledge base". Wanting to narrow access and leaving the list empty does the opposite.

> **The body is raw markup**
> 
> The article body goes straight to the screen and is not cleaned. If you embed a value from outside, escape it yourself.

## Related Articles

- Knowledge Base and Notification Hooks
- [Notification Hooks](https://dev.wisecp.com/en/notification-hooks)
- [How Hooks Work](https://dev.wisecp.com/en/how-hooks-work)
