Skip to content

Part 6: Activity Points and Activity Events#

In this part of our tutorial series, we use the person information added in the previous part to award activity points to users adding new pieces of information and to also create activity events for these pieces of information.

Package Functionality#

In addition to the existing functions from part 5, the package will provide the following functionality after this part of the tutorial:

  • Users are awarded activity points for adding new pieces of information for people.
  • If users add new pieces of information for people, activity events are added which are then shown in the list of recent activities.

Used Components#

In addition to the components used in previous parts, we will use the user activity points API and the user activity events API.

Package Structure#

The package will have the following file structure excluding unchanged files from previous parts:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
├── files
│   └── lib
│       ├── data
│       │   └── person
│       │       ├── PersonAction.class.php
│       │       └── information
│       │           ├── PersonInformation.class.php
│       │           └── PersonInformationAction.class.php
│       └── system
│           ├── user
│           │   └── activity
│           │       └── event
│           │           └── PersonInformationUserActivityEvent.class.php
│           └── worker
│               ├── PersonInformationRebuildDataWorker.class.php
│               └── PersonRebuildDataWorker.class.php
├── eventListener.xml
├── language
│   ├── de.xml
│   └── en.xml
└── objectType.xml

For all changes, please refer to the source code on GitHub.

User Activity Points#

The first step to support activity points is to register an object type for the com.woltlab.wcf.user.activityPointEvent object type definition for created person information and specify the default number of points awarded per piece of information:

objectType.xml
1
2
3
4
5
<type>
    <name>com.woltlab.wcf.people.information</name>
    <definitionname>com.woltlab.wcf.user.activityPointEvent</definitionname>
    <points>2</points>
</type>

Additionally, the phrase wcf.user.activityPoint.objectType.com.woltlab.wcf.people.information (in general: wcf.user.activityPoint.objectType.{objectType}) has to be added.

The activity points are awarded when new pieces are created via PersonInformation::create() using UserActivityPointHandler::fireEvent() and removed in PersonInformation::create() via UserActivityPointHandler::removeEvents() if pieces of information are deleted.

Lastly, we have to add two components for updating data: First, we register a new rebuild data worker

objectType.xml
1
2
3
4
5
<type>
    <name>com.woltlab.wcf.people.information</name>
    <definitionname>com.woltlab.wcf.rebuildData</definitionname>
    <classname>wcf\system\worker\PersonInformationRebuildDataWorker</classname>
</type>
files/lib/system/worker/PersonInformationRebuildDataWorker.class.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
<?php

namespace wcf\system\worker;

use wcf\data\person\information\PersonInformationList;
use wcf\system\user\activity\point\UserActivityPointHandler;

/**
 * Worker implementation for updating person information.
 *
 * @author  Matthias Schmidt
 * @copyright   2001-2021 WoltLab GmbH
 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 * @package WoltLabSuite\Core\System\Worker
 *
 * @method  PersonInformationList   getObjectList()
 */
class PersonInformationRebuildDataWorker extends AbstractRebuildDataWorker
{
    /**
     * @inheritDoc
     */
    protected $objectListClassName = PersonInformationList::class;

    /**
     * @inheritDoc
     */
    protected $limit = 500;

    /**
     * @inheritDoc
     */
    protected function initObjectList()
    {
        parent::initObjectList();

        $this->objectList->sqlOrderBy = 'person_information.personID';
    }

    /**
     * @inheritDoc
     */
    public function execute()
    {
        parent::execute();

        if (!$this->loopCount) {
            UserActivityPointHandler::getInstance()->reset('com.woltlab.wcf.people.information');
        }

        if (!\count($this->objectList)) {
            return;
        }

        $itemsToUser = [];
        foreach ($this->getObjectList() as $personInformation) {
            if ($personInformation->userID) {
                if (!isset($itemsToUser[$personInformation->userID])) {
                    $itemsToUser[$personInformation->userID] = 0;
                }

                $itemsToUser[$personInformation->userID]++;
            }
        }

        UserActivityPointHandler::getInstance()->fireEvents(
            'com.woltlab.wcf.people.information',
            $itemsToUser,
            false
        );
    }
}

which updates the number of instances for which any user received person information activity points. (This data worker also requires the phrases wcf.acp.rebuildData.com.woltlab.wcf.people.information and wcf.acp.rebuildData.com.woltlab.wcf.people.information.description).

Second, we add an event listener for UserActivityPointItemsRebuildDataWorker to update the total user activity points awarded for person information:

eventListener.xml
1
2
3
4
5
6
<eventlistener name="execute@wcf\system\worker\UserActivityPointItemsRebuildDataWorker">
    <eventclassname>wcf\system\worker\UserActivityPointItemsRebuildDataWorker</eventclassname>
    <eventname>execute</eventname>
    <listenerclassname>wcf\system\event\listener\PersonUserActivityPointItemsRebuildDataWorkerListener</listenerclassname>
    <environment>admin</environment>
</eventlistener>
files/lib/system/event/listener/PersonUserActivityPointItemsRebuildDataWorkerListener.class.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<?php

namespace wcf\system\event\listener;

use wcf\system\database\util\PreparedStatementConditionBuilder;
use wcf\system\user\activity\point\UserActivityPointHandler;
use wcf\system\WCF;
use wcf\system\worker\UserActivityPointItemsRebuildDataWorker;

/**
 * Updates the user activity point items counter for person information.
 *
 * @author  Matthias Schmidt
 * @copyright   2001-2021 WoltLab GmbH
 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 * @package WoltLabSuite\Core\System\Event\Listener
 */
class PersonUserActivityPointItemsRebuildDataWorkerListener extends AbstractEventListener
{
    protected function onExecute(UserActivityPointItemsRebuildDataWorker $worker): void
    {
        $objectType = UserActivityPointHandler::getInstance()
            ->getObjectTypeByName('com.woltlab.wcf.people.information');

        $conditionBuilder = new PreparedStatementConditionBuilder();
        $conditionBuilder->add('user_activity_point.objectTypeID = ?', [$objectType->objectTypeID]);
        $conditionBuilder->add('user_activity_point.userID IN (?)', [$worker->getObjectList()->getObjectIDs()]);

        $sql = "UPDATE  wcf" . WCF_N . "_user_activity_point user_activity_point
                SET     user_activity_point.items = (
                            SELECT  COUNT(*)
                            FROM    wcf" . WCF_N . "_person_information person_information
                            WHERE   person_information.userID = user_activity_point.userID
                        ),
                        user_activity_point.activityPoints = user_activity_point.items * ?
                {$conditionBuilder}";
        $statement = WCF::getDB()->prepareStatement($sql);
        $statement->execute(\array_merge(
            [$objectType->points],
            $conditionBuilder->getParameters()
        ));
    }
}

User Activity Events#

To support user activity events, an object type for com.woltlab.wcf.user.recentActivityEvent has to be registered with a class implementing wcf\system\user\activity\event\IUserActivityEvent:

objectType.xml
1
2
3
4
5
<type>
    <name>com.woltlab.wcf.people.information</name>
    <definitionname>com.woltlab.wcf.user.recentActivityEvent</definitionname>
    <classname>wcf\system\userctivity\event\PersonInformationUserActivityEvent</classname>
</type>
files/lib/system/user/activity/event/PersonInformationUserActivityEvent.class.php
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<?php

namespace wcf\system\user\activity\event;

use wcf\data\person\information\PersonInformationList;
use wcf\system\SingletonFactory;
use wcf\system\WCF;

/**
 * User activity event implementation for person information.
 *
 * @author  Matthias Schmidt
 * @copyright   2001-2021 WoltLab GmbH
 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
 * @package WoltLabSuite\Core\System\User\Activity\Event
 */
class PersonInformationUserActivityEvent extends SingletonFactory implements IUserActivityEvent
{
    /**
     * @inheritDoc
     */
    public function prepare(array $events)
    {
        $objectIDs = \array_column($events, 'objectID');

        $informationList = new PersonInformationList();
        $informationList->setObjectIDs($objectIDs);
        $informationList->readObjects();
        $information = $informationList->getObjects();

        foreach ($events as $event) {
            if (isset($information[$event->objectID])) {
                $personInformation = $information[$event->objectID];

                $event->setIsAccessible();
                $event->setTitle(
                    WCF::getLanguage()->getDynamicVariable(
                        'wcf.user.profile.recentActivity.personInformation',
                        [
                            'person' => $personInformation->getPerson(),
                            'personInformation' => $personInformation,
                        ]
                    )
                );
                $event->setDescription($personInformation->getFormattedExcerpt());
            }
        }
    }
}

PersonInformationUserActivityEvent::prepare() must check for all events whether the associated piece of information still exists and if it is the case, mark the event as accessible via the setIsAccessible() method, set the title of the activity event via setTitle(), and set a description of the event via setDescription() for which we use the newly added PersonInformation::getFormattedExcerpt() method.

Lastly, we have to add the phrase wcf.user.recentActivity.com.woltlab.wcf.people.information, which is shown in the list of activity events as the type of activity event.


Last update: 2021-06-03