Jump to content
celfred

Hook to update connected users

Recommended Posts

Hello,

I'm struggling with hooks to try and do this : adding user page to a page field when visiting a particular page on my website. In fact, I want to have a list of connected users on a particular page (called meeting-hall).

I've tried all kinds of things (which I don't really understand, sorry) but the latest is this :

	  $wire->addHookBefore('Page::render', function($e) {
    $page = $e->arguments(0);
    if ($page->is("name=meeting-hall")) {
      $page->connected->add($user); // connected is a Page field
      $page->save();
    } else {
      $meeting = wire("pages")->get("name=meeting-hall");
      $meeting->connected->remove($user);
      $meeting->save();
    }
  });
	

I put this in my _init.php file but nothing works...

The best I managed was updating my 'connected' field when the user loads the 'meeting hall' page by having this on the page template :

    if ($user->isLoggedin()) {
      $page->connected->add($user);
      $page->of(false);
      $page->save();
      $page->of(true);
    }

But I wanted to remove the user when he or she leaves the page... hence my thought about using hooks...

If you can give me a hint, I'd greatly appreciate !

Share this post


Link to post
Share on other sites

Hi @celfred

Could you please describe the bigger picture of what you are doing? Is this some kind of chatroom like thing?

Share this post


Link to post
Share on other sites

Hi @bernhard,

Thanks for your interest.

Actually, it is not a big thing. I have a collaborative pad that I share with my middle-school students that resides on the Meeting Hall page. I have used the database session manager module to get a liist of logged-in users so that when someone is on the homepage, he or she can see if others are connected at the same time. And now, I would like to simply add a 'bell' next to the connected names when they actually are ON the Meeting Hall page so others can know they are there.

So my idea was to add a Page field on my meeting-hall template with connected users updating... as they come and go... Maybe that's a lot of 'fuss' for not so much, though...

 

Share this post


Link to post
Share on other sites

Thx, that gives us a good picture. I'm just asking because determining the "online" state like this is not really reliable. What if the user opens another page on the website in a second tab? This would remove the online state on the meeting-hall but this window could still be open. Not sure how critical this online state is and I don't want to overcomplicate things. Just want to make sure you have thought of that 😉 

An easy solution could be to save the timestamp of the last activity of the user on that meeting-hall page. Then you could show "last active 5min ago" and if that time goes beyond lets say 10min you show "offline" or hide the user from that list.

Maybe this would also make your setup easier, because you could simply add the timestamp to the users template and populate that whenever a user interacts with the meeting-hall page. A list of all active users would then be $pages->find("template=user, meetinghalltimestamp>$nowMinusTenMinutes")

 

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

Wouah. Thanks a lot for your explanations !

I had thought of the tabs issue, but couldn't think of another possibility 🙂 And your timestamp idea sounds great !

I'll go ahead and try and do that instead, but it'll take ME some time so that's for tomorrow I guess ! But the more I think about it, the more I see how this can be made YOUR way. Thanks !

Share this post


Link to post
Share on other sites

The problem is less so multiple tabs and more so spotty internet and detecting people leaving. The server cannot know if people disconnected due to whatever reason unless being constantly in communication with them(e.g. a heartbeat msg) and using timeouts to mark someone as offline once responses are late or missing. This get‘s even trickier with multiple servers as people could have connection to one of the servers but not the others. So someone is only offline of there‘s no connection to non of the servers. I‘m not sure if this is a problem for your usecase, but that‘s what‘s tricky about even just basic availability lists of users. There are solutions for such problems, but personally I‘d not even go with php to solve them, so it‘s nothing pw can help you with greatly.

Share this post


Link to post
Share on other sites

Thanks @LostKobrakai. I understand the problem and my concern was really a very basic thing, even if some use cases 'pass through'. It is nothing professional 😉  and @bernhard's solution seems to the point for me : if another user loads any page, he or she will get an indicator of how many connected users are online and for those being active on the Meeting Hall, let's say within the last 10 minutes, a 'bell' will be added to show this recent activity.

My only left concern now is when people are on the Meeting Hall page for more than 10 minutes, using the collaborative pad (which is embedded from another server), hence having no 'activity' on the page. I should need to update the timestamp. I'll have to see if it's possible to have a 'cron' function while connected on a particular page  or simply add a button on which a user can click to update his or her timestamp to say "I'm here !"...

Again, much work (for me) for not so much effect... 🙂

But thanks a lot for your help !

Share this post


Link to post
Share on other sites
3 minutes ago, celfred said:

My only left concern now is when people are on the Meeting Hall page for more than 10 minutes, using the collaborative pad (which is embedded from another server), hence having no 'activity' on the page. I should need to update the timestamp. I'll have to see if it's possible to have a 'cron' function while connected on a particular page  or simply add a button on which a user can click to update his or her timestamp to say "I'm here !"...

Just add some javascript that polls the page in the background every 5 minutes or so: https://stackoverflow.com/a/8682723/6370411

Share this post


Link to post
Share on other sites

Well, I'm getting there... almost !

I used JS to update timestamp. Everything works as expected, except... (sorry...) : only admin can list users ? The aforementioned code by @bernhard :

	$pages->find("template=user, meetinghalltimestamp>$nowMinusTenMinutes")
	

only returns result when I'm logged in as admin.

Should I set my timestamp field on another template or is there a way to 'open' this and is it a recommended practice ?

EDIT : I saw that adding 'include=all' to my request made it.

Thanks for all your help !

Share this post


Link to post
Share on other sites
2 hours ago, celfred said:

I understand the problem and my concern was really a very basic thing, even if some use cases 'pass through'.

I can fully understand that. I just wanted to give the full picture. How much of it matters to your case is nothing I can decide 🙂

  • Thanks 1

Share this post


Link to post
Share on other sites
7 hours ago, Pixrael said:

How the current connected users at that page knows about new (or ended) user connections? I think you need a solution with Websockets, check https://socket.io/

Update: for php check: http://socketo.me/

Except it requires composer to install.  😱

Share this post


Link to post
Share on other sites

Thanks for the links.  I haven't looked into sockets in quite some time, but I do have an upcoming project to use this with. Thank you!

  • Like 1

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Andi
      Continuing my journey into PW hooks, I'm trying to find a way to retrieve all images from a page that explicitly *do not* have a certain tag (or tags) attached to them.
      Found this post from 2015
      But I'm wondering if there's a more elegant way to go about this.
      Let's say I have a multi-image field called "images_header" and instead of
      $page->images_header->findTag('mytag'); I would like to do this:
      $page->images_header->excludeTag('mytag'); So I'd be able to do
      // find images that don't have the tag "mytag" $images = $page->images_header->excludeTag('mytag'); // check if there's any images if (count($images)>0) { // do something.. } Would this be possible by hooking into Pagefiles somehow?
      There's this bit in /wire/core/Pagefiles.php Line 626 that I'd basically just need to reverse (or at least in my mind 😄 )
      public function findTag($tag) { $items = $this->makeNew(); foreach($this as $pagefile) { if($pagefile->hasTag($tag)) $items->add($pagefile); } return $items; } Any ideas on how this could be done in a graceful manner?
      Thanks in advance!
    • By Andi
      Getting a little deeper into the ProcessWire state-of-mind here. I seriously think I wouldn't have come back to webdev if it wasn't for this wonderful little gem of a CMS.
      I have an "Options" field added to all users on a site. If the user has anything other then "default" selected, I would like to show a permanent message in the admin like the one in the screenshot, only so that the user can't close it. As a friendly reminder that he changed that option from default to something crazy 🙂
      I've read up on how to send messages to users, but where would I hook into to make this show up all the time in the backend?
      https://processwire.com/api/ref/wire/message/
      Thanks in advance!

    • By celfred
      Hello,
       
      Here's what I'm trying to achieve : I have a textarea field that is frontend editable on any page I want to. If a user with a specific role updates it, I want to tick a checkbox field on the parent page where the textarea resides. So I tried to hook as follows in my ready.php file :

        $this->addHookBefore('Fieldtype::savePageField', function(HookEvent $event) {       $page = $event->argument[0];       $field = $event->argument[1];       if($this->user->hasRole('teacher')) {      $page->alertBox = 1;       } // DEBUG HERE (bd($page),  l($field)... ????   }); Of course (!) it doesn't work (yet !) but the thing is I have no idea how to debug this since my bd() never triggers. I've tried the 'Event interceptor' of Tracy debugger which helped me setting up my hook. I guess my $page and $field are correct... but how could I go any further ?
      The road is long to become a dev (when you're not one 🙂 )...
      For further information in case someone wonders : I'd like to set up a textarea that is modified by a user (having a 'player' role, ie 'student'). When front-end modified, I wish it would automatically alert me (the teacher) by ticking a box. So when I log in with my teacher role, I get a list of all textareas I have to read over. When I read/correct/update them (front-end), I would like my hook to automatically untick the box to remove it from my list. In other words, the 'textarea' status should go back and forth according to who modified it last. For the time being, I have managed to make it work with a checkbox that the user has to manually tick/untick, but I've noticed many kids forget to tick the box so they edit their text and I don't get a notice 😞
      Thanks if anyone takes time to try and help me on that one !
    • By Gadgetto
      Hi there,
      I'd like to prevent duplicate values in a specific page field (FieldtypeText) across all pages. The hook should also prevent saving of the page if a duplicate value is detected. Therefore I created two hook methods but I can't get it to work properly.
      Here are both hooks placed in init() method of an autoloader module:
      The first hook is to initially preset the field with the page id. The field value can be customized by the admin before saving.
      The second hook should check if the value is unique.
      If I create a new page and try to save, the WireException is already triggered in first step of page creation, where you enter the page title: SKU [] is already in use
      If I check the value of $sku (with Tracy) it's always empty!
      Any idea what could be wrong?
      EDIT: if I remove the second hook, the field value is correctly preset with the page id!
      $this->addHookAfter('Pages::added', $this, 'presetProductFields', ['priority' => 99]); $this->addHookAfter('Pages::saveReady', $this, 'checkSKUUnique', ['priority' => 101]); And this are the hook methods:
      public function presetProductFields(HookEvent $event) { $snipwire = $this->wire('snipwire'); if (!$snipwire) return; $page = $event->arguments(0); if ($snipwire->isProductTemplate($page->template)) { if ($page->hasfield('snipcart_item_id')) $page->setAndSave('snipcart_item_id', $page->id); } } /** * Check if the SKU value is unique across all product pages. * (Method triggered after Pages saveReady -> just before page is saved) * * @throws WireException * */ public function checkSKUUnique(HookEvent $event) { $snipwire = $this->wire('snipwire'); if (!$snipwire) return; $page = $event->arguments(0); if ($snipwire->isProductTemplate($page->template)) { $field = $page->getField('snipcart_item_id'); $sku = $page->snipcart_item_id; // SKU field value bd($sku); if ($page->isChanged('snipcart_item_id')) { $exists = $this->wire('pages')->get("snipcart_item_id=$sku"); if ($exists->id) { // value is not unique! $error = $this->_('SKU must be unique'); $exception = sprintf( $this->_('SKU [%s] is already in use'), $sku ); $inputfield = $page->getInputfield($field); $inputfield->error($error); throw new WireException($exception); // Prevent saving of non-unique value! } } } }  
    • By t0b1
      Hello there, and thanks for ProcessWire!
      I'm getting to know ProcessWire while doing my first project using it and I really like it so far.
      The challenge I'm facing right now is the following:
      I have a One-Pager using fullpage.js, realized as a single PW-Page containing a Repeater Field where each Repeater Item is one Section.
      Some Sections are supposed to have a little menu at the Top which references/links to different Sections of the Website, so I wanted to use a Checkbox Field "Top Menu" to decide if a Section gets a Menu and a Page Reference Field to choose the different Sections (Repeater Items) it should contain.
      I've already accomplished this by pasting the following code into /site/ready.php:
       
      $wire->addHookAfter('InputfieldPage::getSelectablePages', function($event) { if($event->object->hasField == 'top_menu_entries') { $page = $event->arguments('page'); if($page instanceof RepeaterPage) $page = $page->getForPage(); $event->return = $page->; } });  
      The only problem that still remains is that when I select the Entry of the Repeater Item itself it doesnt save the selection, meaning after I saved it's unselected again.
      On some Sections I do want a Menu-Entry for the Section itself though (which would be styled differently and not link anywhere) for Continuity-Reasons, any ideas on how to achieve that?
×
×
  • Create New...