User Activity by Xon

User Activity by Xon 2.10.0

No permission to download

Silmarillion

Active member
Hello Xon,

I have only now noticed that Redis and Caching are necessary and recommended for this addon. I don't use either (Redis is not possible on my managed server). Nevertheless, the addon works. Should I still uninstall it?
 

Xon

Well-known member
This add-on works without redis, but it comes down to how busy your site is. With a few visitors that will be fine, but the busier the site the more I recommend using a caching backend.
 

Silmarillion

Active member
Ok, then I'm reassured. We currently never have more than 3000 unique users per day. On its own server. That shouldn't be a problem I guess. Does the add-on cause an increase in data that I should delete at regular intervals?

Two questions, Xon: Zend OPcache is running on our server. Could you also cache your add-on with it? I also use Cloudflare. Maybe User Activity
data caching also works with CF?

Best regards
 

Xon

Well-known member
"opcache" only caches php bytcode, not user data. Cloudflare cache the entire page. These are different from XF's caching system
 

Alpha1

Well-known member
I get this when trying to upgrade from XF1:

ErrorException: Batch install error: [E_WARNING] ZipArchive::eek:pen(): Empty string as source

src/XF/Service/AddOnArchive/Extractor.php:36

Generated by: Alfa1
May 1, 2021 at 8:53 AM

Stack trace

#0 [internal function]: XF::handlePhpError(2, '[E_WARNING] Zip...', '/home/nginx/dom...', 36, Array)
#1 src/XF/Service/AddOnArchive/Extractor.php(36): ZipArchive->open('')
#2 src/XF/Service/AddOnArchive/Extractor.php(56): XF\Service\AddOnArchive\Extractor->open()
#3 src/XF/Service/AddOnArchive/Extractor.php(125): XF\Service\AddOnArchive\Extractor->zip()
#4 src/XF/Job/AddOnInstallBatch.php(176): XF\Service\AddOnArchive\Extractor->copyFiles(NULL, 0, Object(XF\Timer))
#5 src/XF/Job/AddOnInstallBatch.php(81): XF\Job\AddOnInstallBatch->stepCopy(Object(XF\Timer))
#6 src/XF/Job/Manager.php(258): XF\Job\AddOnInstallBatch->run(8)
#7 src/XF/Job/Manager.php(200): XF\Job\Manager->runJobInternal(Array, 8)
#8 src/XF/Job/Manager.php(116): XF\Job\Manager->runJobEntry(Array, 8)
#9 src/XF/Admin/Controller/Tools.php(120): XF\Job\Manager->runByIds(Array, 8)
#10 src/XF/Mvc/Dispatcher.php(350): XF\Admin\Controller\Tools->actionRunJob(Object(XF\Mvc\ParameterBag))
#11 src/XF/Mvc/Dispatcher.php(257): XF\Mvc\Dispatcher->dispatchClass('XF:Tools', 'RunJob', Object(XF\Mvc\RouteMatch), Object(SV\UserMentionsImprovements\XF\Admin\Controller\Tools), NULL)
#12 src/XF/Mvc/Dispatcher.php(113): XF\Mvc\Dispatcher->dispatchFromMatch(Object(XF\Mvc\RouteMatch), Object(SV\UserMentionsImprovements\XF\Admin\Controller\Tools), NULL)
#13 src/XF/Mvc/Dispatcher.php(55): XF\Mvc\Dispatcher->dispatchLoop(Object(XF\Mvc\RouteMatch))
#14 src/XF/App.php(2326): XF\Mvc\Dispatcher->run()
#15 src/XF.php(488): XF\App->run()
#16 admin.php(13): XF::runApp('XF\\Admin\\App')
#17 {main}

Request state

array(4) {
["url"] => string(33) "/xf/admin.php?tools/run-job"
["referrer"] => string(61) "-xf/admin.php?tools/run-job"
["_GET"] => array(1) {
["tools/run-job"] => string(0) ""
}
["_POST"] => array(3) {
["_xfRedirect"] => string(96) "-xf/admin.php?add-ons/install-from-archive-complete&batch_id=9"
["_xfToken"] => string(8) "********"
["only_ids"] => string(3) "143"
}
}
 

Xon

Well-known member
Nothing about the add-ons zips are special, they are made via XenForo's add-on builder using php7.4. The only reference to that error message is a stupidly old version of XenForo & likely php.

What version of php are you using?
 

Kirby

Well-known member
I think I've discovered a little bug:
If a node does have a node_name set, the activity is not being tracked as param node_id ist not set in this caes when calling XF\Repository\SessionActivity::updateSessionActivity

Would it be possible to add a setting to choose the location for template forum_view?
Could probably use the existing setting with some rewording.

Code:
<h3 class="block-minorHeader">{{ phrase('svUserActivity_users_who_are_viewing_this_x', {'content_type': $contentTypePhrase|to_lower}) }}</h3>
It seems impossible to properly translate this into german:
In german, the first letter of the content type name needs to be uppercase as it is a noun.

Furthermore, translation depends on the grammatical gender ("genus") of the noun - Thema (thread) for example is neutral whereas conversation is female (if translated as Unterhaltung or Konversation).

Would it be possible to change this so an individual phrase is being used for each content type?
That would allow to translate this properly :)
 
Last edited:

Xon

Well-known member
I think I've discovered a little bug:
If a node does have a node_name set, the activity is not being tracked as param node_id ist not set in this caes when calling XF\Repository\SessionActivity::updateSessionActivity
The code actually extracts the observed node_id(s) from the Response's View::getParam variables, and doesn't actually parse it out of the arguments that go into the action.

This pattern allows the code to only run & extract user activity if the user has permission to view the relevant content.

Would it be possible to add a setting to choose the location for template forum_view?
Could probably use the existing setting with some rewording.
The code for viewing user activity in a container (vs the thread/conversation/etc) wasn't an initial goal and was only added after a paid request.

Moving the actual code yourself is very simple;
HTML:
<xf:macro template="UA_ViewContainer_macros"
          name="UserActivity"
          arg-contentTypePhrase="{{ phrase('forum') }}"
          arg-records="{$UA_Records}"/>

This impacts a few places, "Adds activity containers (list)" template mods for a number of categories/nodes/etc, and then wiring up a style property or option. These really should be style properties, but migrating from options to style properties is frustrating and annoying, so I haven't done that.

I'm open to pull requests on github however.

Code:
<h3 class="block-minorHeader">{{ phrase('svUserActivity_users_who_are_viewing_this_x', {'content_type': $contentTypePhrase|to_lower}) }}</h3>
It seems impossible to properly translate this into german:
In german, the first letter of the content type name needs to be uppercase as it is a noun.

Furthermore, translation depends on the grammatical gender ("genus") of the noun - Thema (thread) for example is neutral whereas conversation is female (if translated as Unterhaltung or Konversation).

Would it be possible to change this so an individual phrase is being used for each content type?
That would allow to translate this properly :)
My recommendation is just translate svUserActivity_users_who_are_viewing_this_x as not actually mentioning the current content type which is one thing I've actually been considering.

I'm open to a PR on github if you want to go through all the effort to write essentially every template modification and create about dozen new phrases.
 

Kirby

Well-known member
The code actually extracts the observed node_id(s) from the Response's View::getParam variables, and doesn't actually parse it out of the arguments that go into the action.
Hmm, maybe I am missing smth.?

PHP:
class SessionActivity extends XFCP_SessionActivity
{
    /**
     * @param int $userId
     * @param $ip
     * @param string $controller
     * @param string $action
     * @param array $params
     * @param string $viewState enum('valid','error')
     * @param string $robotKey
     */
    public function updateSessionActivity($userId, $ip, $controller, $action, array $params, $viewState, $robotKey)
    {
        /** @var \SV\UserActivity\Repository\UserActivity $userActivityRepo */
        $userActivityRepo = \XF::repository('SV\UserActivity:UserActivity');
        $visitor = \XF::visitor();
        if ($userActivityRepo->isLogging() && $viewState == 'valid' && $userId === $visitor->user_id)
        {
            $handler = $userActivityRepo->getHandler($controller);
            if (!empty($handler))
            {
                $requiredKey = $handler['id'];
                if (!empty($params[$requiredKey]))
                {
                    $userActivityRepo->bufferTrackViewerUsage($handler['type'], $params[$requiredKey], $handler['activeKey']);
                }

So when this is called for a forum, $handler would be
Code:
[
    'controller' => 'XF\Pub\Controller\Forum',
    'type'       => 'node',
    'id'         => 'node_id',
    'actions'    => ['forum'],
    'activeKey'  => 'forum',
]

As $handler is not empty it would take $handler['id'] (= node_id) as the $requiredKey to look up in $params.

But if the node does have a node_name set, $params['node_id'] would be empty as only $params['node_name'] ist set in this case so $userActivityRepo->bufferTrackViewerUsage never does get called?
 

Xon

Well-known member
As $handler is not empty it would take $handler['id'] (= node_id) as the $requiredKey to look up in $params.

But if the node does have a node_name set, $params['node_id'] would be empty as only $params['node_name'] ist set in this case so $userActivityRepo->bufferTrackViewerUsage never does get called?
After more review, this does looks to be a bug. There where some XF2.0 like handling which wasn't updated to XF2.1 too.

Both should be fixed for the next version.
 

Kirby

Well-known member
Not sure if this is working as designed or a bug?

Settings
Prune chance = 1
Fill factor = 0
Thread contribute limit = 1
Track user activity = Thread, Forum, Category
Display user list = Thread, Forum
Display viewer counts = Index Page Forums, Category View, Sub forum list
Track guests = yes
Track robots = yes

I am the only user that is active and I am viewing a forum with several sub-forums.
I click on every sub-forum link to open in a new tab, close those new tabs again and afterwards refresh the page.

Now every sub forum does show one visitor, although there effectively is none.
 

Xon

Well-known member
The user activity buckets by the online time (ie ~15 minute blocks) to show users as "active" and doesn't actually care about logging out of if you close the tab.

This is more of a performance thing as you really only notice it with low activity numbers.
 

Xon

Well-known member
Xon updated User Activity by Xon with a new update entry:

2.9.0 - Feature & bugfix update

  • php 7+ type hinting
  • Force global namespace for functions which are known to be optimizable to bytecode in php, or known global functions to avoid a current namespace lookup for the function.
  • Fix forum activity not registering as expected when a node_name is set on the forum
  • Fix logging XF2.1+ reaction events as thread activity & remove old XF2.0 like handling for posts.
  • Replace single svUserActivity_users_who_are_viewing_this_x phrase with various...

Read the rest of this update entry...
 

dethfire

Well-known member
Getting a ton of these since update

  • LogicException: Macro public:UA_ViewContainer_macros :: UserActivity() error: Macro argument contentTypePhrase is required and no value was provided
  • src/XF/Template/Templater.php:896
  • Generated by: Unknown account
  • Aug 24, 2021 at 7:13 AM
 

Xon

Well-known member
Getting a ton of these since update

  • LogicException: Macro public:UA_ViewContainer_macros :: UserActivity() error: Macro argument contentTypePhrase is required and no value was provided
  • src/XF/Template/Templater.php:896
  • Generated by: Unknown account
  • Aug 24, 2021 at 7:13 AM
Is this just during the upgrade if so that is expected.

Otherwise I need the stack trace.
 

dethfire

Well-known member
Even after

Stack trace​

#0 src/XF/Template/MacroState.php(60): XF\Template\Templater->mergeMacroArguments(Array, Array, Array)
#1 src/XF/Template/Templater.php(789): XF\Template\MacroState->getAvailableVars(Object(SV\StandardLib\XF\Template\Templater), Array, Array)
#2 internal_data/code_cache/templates/l1/s12/public/thread_view.php(948): XF\Template\Templater->callMacro('UA_ViewContaine...', 'UserActivity', Array, Array)
#3 src/XF/Template/Templater.php(1644): XF\Template\Templater->{closure}(Object(SV\StandardLib\XF\Template\Templater), Array, Object(XF\Template\ExtensionSet))
#4 src/XF/Template/Template.php(24): XF\Template\Templater->renderTemplate('thread_view', Array)
#5 src/XF/Mvc/Renderer/Html.php(48): XF\Template\Template->render()
#6 src/XF/Mvc/Dispatcher.php(458): XF\Mvc\Renderer\Html->renderView('XF:Thread\\View', 'public:thread_v...', Array)
#7 src/XF/Mvc/Dispatcher.php(440): XF\Mvc\Dispatcher->renderView(Object(XF\Mvc\Renderer\Html), Object(XF\Mvc\Reply\View))
#8 src/XF/Mvc/Dispatcher.php(400): XF\Mvc\Dispatcher->renderReply(Object(XF\Mvc\Renderer\Html), Object(XF\Mvc\Reply\View))
#9 src/XF/Mvc/Dispatcher.php(58): XF\Mvc\Dispatcher->render(Object(XF\Mvc\Reply\View), 'html')
#10 src/XF/App.php(2344): XF\Mvc\Dispatcher->run()
#11 src/XF.php(512): XF\App->run()
#12 index.php(20): XF::runApp('XF\\Pub\\App')
#13 {main}
 

Xon

Well-known member
Even after

Stack trace​

#0 src/XF/Template/MacroState.php(60): XF\Template\Templater->mergeMacroArguments(Array, Array, Array)
#1 src/XF/Template/Templater.php(789): XF\Template\MacroState->getAvailableVars(Object(SV\StandardLib\XF\Template\Templater), Array, Array)
#2 internal_data/code_cache/templates/l1/s12/public/thread_view.php(948): XF\Template\Templater->callMacro('UA_ViewContaine...', 'UserActivity', Array, Array)
#3 src/XF/Template/Templater.php(1644): XF\Template\Templater->{closure}(Object(SV\StandardLib\XF\Template\Templater), Array, Object(XF\Template\ExtensionSet))
#4 src/XF/Template/Template.php(24): XF\Template\Templater->renderTemplate('thread_view', Array)
#5 src/XF/Mvc/Renderer/Html.php(48): XF\Template\Template->render()
#6 src/XF/Mvc/Dispatcher.php(458): XF\Mvc\Renderer\Html->renderView('XF:Thread\\View', 'public:thread_v...', Array)
#7 src/XF/Mvc/Dispatcher.php(440): XF\Mvc\Dispatcher->renderView(Object(XF\Mvc\Renderer\Html), Object(XF\Mvc\Reply\View))
#8 src/XF/Mvc/Dispatcher.php(400): XF\Mvc\Dispatcher->renderReply(Object(XF\Mvc\Renderer\Html), Object(XF\Mvc\Reply\View))
#9 src/XF/Mvc/Dispatcher.php(58): XF\Mvc\Dispatcher->render(Object(XF\Mvc\Reply\View), 'html')
#10 src/XF/App.php(2344): XF\Mvc\Dispatcher->run()
#11 src/XF.php(512): XF\App->run()
#12 index.php(20): XF::runApp('XF\\Pub\\App')
#13 {main}
Check that the UA_ViewContainer_macros template isn't out of date, as it you probably have customizations which need merging.
 
Top