
Description
This modification will help you add additional filters (active/expired/online/recently online) to the Users page in Nextpost.
- Active accounts
- Expired accounts
- Online (User which active in last 20 seconds)
- Recently Online (User which active in last 15 minutes)

Changelog
Version 1.0 - 7.06.2020 - Initial release.
Instruction
Suggestion: use Microsoft Visual Studio Code for code editing and profiling. All your syntax error will be highlighted.
Before we start making changes to the code, I strongly recommend you to backup the source files so that you do not have any problems if something goes wrong.
The list of files that we will change:
/app/controllers/UsersController.php /app/models/UserModel.php /app/views/fragments/google-analytics.fragment.php /app/views/fragments/aside-search-form.fragment.php /app/views/fragments/users.fragment.php /app/views/users.php /app/inc/routes.inc.php /assets/css/core.css
Step 1
Go to phpMyAdmin (database management) to your Nextpost database and execute the following commands:
ALTER TABLE `np_users` ADD `is_online` INT(1) NOT NULL ; ALTER TABLE `np_users` ADD `last_activity_time` INT(11) NOT NULL ;

Step 2.1
Open file /app/models/UserModel.php and find function public function extendDefaults() and add 2 new lines:
"is_online" => "0", "last_activity_time" => "0",
On the screenshots for Step 2.1 – 2.3, I have some different values, that you can don’t have. For example fb_userid and g_userid it’s a part of Fast Social Login modification. In the current modification, you need only 2 values is_online and last_activity_time.

Step 2.2
Open file /app/models/UserModel.php and find function public function insert() and add 2 new lines:
"is_online" => $this->get("is_online"), "last_activity_time" => $this->get("last_activity_time")

Step 2.3
Open file /app/models/UserModel.php and find function public function update() and add 2 new lines:
"is_online" => $this->get("is_online"), "last_activity_time" => $this->get("last_activity_time")

Step 2.4
Open file /app/models/UserModel.php and before function public function delete() and add new function isOnline():
/** * Check if user online or not * @return boolean */ public function isOnline() { if ($this->isAvailable() && $this->get("is_online") && ($this->get("last_activity_time") >= time() - 60)) { return true; } return false; }

Step 3
Open file /app/controllers/UsersController.php and find that code:
// Get Users $Users = Controller::model("Users"); $Users->search(Input::get("q")) ->setPageSize(20) ->setPage(Input::get("page")) ->orderBy("id","DESC") ->fetchData();
Replace with new one:
// Show active users $filters_active = \Input::get("filters_active"); if ($filters_active == "yes") { $AuthUser->set("data.users_filters.active", true) ->save(); } elseif ($filters_active == "no") { $AuthUser->set("data.users_filters.active", false) ->save(); } $filters_active_cv = $AuthUser->get("data.users_filters.active"); // Show expired users $filters_expired = \Input::get("filters_expired"); if ($filters_expired == "yes") { $AuthUser->set("data.users_filters.expired", true) ->save(); } elseif ($filters_expired == "no") { $AuthUser->set("data.users_filters.expired", false) ->save(); } $filters_expired_cv = $AuthUser->get("data.users_filters.expired"); // Show online users $filters_online = \Input::get("filters_online"); if ($filters_online == "yes") { $AuthUser->set("data.users_filters.online", true) ->save(); } elseif ($filters_online == "no") { $AuthUser->set("data.users_filters.online", false) ->save(); } $filters_online_cv = $AuthUser->get("data.users_filters.online"); // Show recently online users $filters_recently = \Input::get("filters_recently"); if ($filters_recently == "yes") { $AuthUser->set("data.users_filters.recently", true) ->save(); } elseif ($filters_recently == "no") { $AuthUser->set("data.users_filters.recently", false) ->save(); } $filters_recently_cv = $AuthUser->get("data.users_filters.recently"); // Get Users $Users = Controller::model("Users"); $Users->search(Input::get("q")); if ($filters_active_cv) { $Users->where("expire_date", ">=", date("Y-m-d H:i:s")) ->where("is_active", "=", 1); } if ($filters_expired_cv) { $Users->where("expire_date", "<", date("Y-m-d H:i:s")); } if ($filters_online_cv) { $Users->where("is_online", "=", "1") ->where("last_activity_time", ">=", time() - 60); } if ($filters_recently_cv) { $Users->where("is_online", "=", "1") ->where("last_activity_time", ">=", time() - 900); } $Users->setPageSize(25) ->setPage(Input::get("page")) ->orderBy("id","DESC") ->fetchData();
Step 4
Open file /assets/css/core.css and to the bottom of the file add new CSS styles:

/** * Users Tab Filters: active, expired and online status */ .js-filter-active.active, .darkside .js-filter-active.active { background-color: #2a68e9; color: #fff; border-color: #2a68e9; } .js-filter-expired.active, .darkside .js-filter-expired.active { background-color: #2a68e9; color: #fff; border-color: #2a68e9; } .js-filter-online.active, .darkside .js-filter-online.active { background-color: #2a68e9; color: #fff; border-color: #2a68e9; } .button.u-small { padding: 6px 10px; line-height: 1; } .user-status-badge { width: 3px; height: 3px; position: absolute; top: 46px; left: 62px; line-height: 16px; background-color: #e0e0e0; border-radius: 50%; font-size: 14px; box-shadow: 0 0 0 3px #fafafa; display: inline-block; padding: 3px; margin: 0; } .darkside .user-status-badge { box-shadow: 0 0 0 2px #181818; background-color: #333333; } .status-online, .darkside .status-online, .js-list-item.active .status-online { background-color: #058C15 !important; } .user-status-badge.status-online:hover { background-color: #058C15 !important; } .user-status-badge:hover { background-color: #2b67e9 !important; } .user-status-tooltip { display: block; cursor: pointer; width: 14px; height: 14px; position: relative; padding: 0; margin: 0; left: -5px; top: -5px; border-radius: 50%; z-index: 99999; } .js-list-item.active .user-status-badge { background-color: #9b9b9b; box-shadow: 0 0 0 2px rgb(230, 230, 230); } .darkside .js-list-item.active .user-status-badge { background-color: #9b9b9b; box-shadow: 0 0 0 2px #272727; } .user-status-tooltip-link { width: 36px; height: 36px; } .button.u-small { padding: 6px 10px; line-height: 1; }
Step 5
Open file /app/views/fragments/google-analytics.fragment.php and add new code to the bottom of that file:

<?php if (isset($AuthUser)): ?> <script> var logging_time = new Date().getTime(); var _loggingClientEvents = function(online = true) { logging_time = new Date().getTime(); $.ajax({ url: "<?= APPURL."/logging_client_events" ?>", type: 'POST', dataType: 'json', data: { action: "last_activitity", is_online: online ? true : false } }); } // Log last activitity time _loggingClientEvents(); $(window).on("focus blur touchstart click mouseenter", function(event) { if (event.type == "focus") { _loggingClientEvents(); } else if (event.type == "blur") { _loggingClientEvents(false); } else if (event.type == "touchstart" || event.type == "click" || event.type == "mouseenter") { if (logging_time + 20*1000 <= new Date().getTime()) { _loggingClientEvents(); } } }); </script> <?php endif ?>
Step 6
Open file /app/inc/routes.inc.php and find that code:
// Free Trial Package App::addRoute("GET|POST", "/packages/trial/?", "TrialPackage");
Replace him with that:
// Free Trial Package App::addRoute("GET|POST", "/packages/trial/?", "TrialPackage"); // Logging Client Events App::addRoute("GET|POST", "/logging_client_events/?", "LoggingClientEvents");
Step 7
Download file LoggingClientEventsController.php and upload him to /app/controllers/.
IMPORTANT: Please double check your permissions of the file, it’s should be 777.
Step 8
Open file /app/views/users.php and find that code:
NextPost.UserForm();
Replace this code with that:
NextPost.UserForm(); NextPost.UsersFilters(); window.loadmore.success = function($item) { NextPost.Tooltip(); }
Step 9
Open file /app/views/fragments/aside-search-form.fragment.php and re-place all content with code from that file.
Step 10
Open file /app/views/fragments/users.fragment.php and re-place all content with code from that file.
Step 11
Open file /assets/js/core.js and to the bottom of the file add new code, which you can find below:

/** * Users Filters */ NextPost.UsersFilters = function() { /** * Active users filtration */ var search_timer; var search_xhr; var $form = $(".skeleton-aside .search-box"); $("body").on("click", "a.js-filter-active", function() { var active_btn = $(".js-filter-active"); var expired_btn = $(".js-filter-expired"); var active_inp = $form.find(":input[name='filters_active']"); var expired_inp = $form.find(":input[name='filters_expired']"); var search_query = $form.find(":input[name='q']"); if (active_inp.val() == 0) { active_inp.val(1); active_btn.removeClass('button--light-outline'); active_btn.addClass('active'); } else { active_inp.val(0) active_btn.addClass('button--light-outline'); active_btn.removeClass('active'); } expired_inp.val(0); expired_btn.addClass('button--light-outline'); expired_btn.removeClass('active'); if (search_xhr) { // Abort previous ajax request search_xhr.abort(); } if (search_timer) { clearTimeout(search_timer); } data = $.param({ filters_active: (active_inp.val() == 1) ? "yes" : "no", filters_expired: (expired_inp.val() == 1) ? "yes" : "no" }); if (search_query.val() != '') { data += '&' + $.param({ q: search_query.val(), }); } var duration = 200; search_timer = setTimeout(function(){ search_query.addClass("onprogress"); $.ajax({ url: $form.attr("action"), type: $form.attr("method"), dataType: 'html', data: data, complete: function() { search_query.removeClass('onprogress'); }, success: function(resp) { $resp = $(resp); if ($resp.find(".skeleton-aside .js-search-results").length == 1) { $(".skeleton-aside .js-search-results") .html($resp.find(".skeleton-aside .js-search-results").html()); NextPost.Tooltip(); } } }); }, duration); }); /** * Active users filtration */ var search_timer; var search_xhr; var $form = $(".skeleton-aside .search-box"); $("body").on("click", "a.js-filter-expired", function() { var active_btn = $(".js-filter-active"); var expired_btn = $(".js-filter-expired"); var active_inp = $form.find(":input[name='filters_active']"); var expired_inp = $form.find(":input[name='filters_expired']"); var search_query = $form.find(":input[name='q']"); if (expired_inp.val() == 0) { expired_inp.val(1); expired_btn.removeClass('button--light-outline'); expired_btn.addClass('active'); } else { expired_inp.val(0) expired_btn.addClass('button--light-outline'); expired_btn.removeClass('active'); } active_inp.val(0); active_btn.addClass('button--light-outline'); active_btn.removeClass('active'); if (search_xhr) { // Abort previous ajax request search_xhr.abort(); } if (search_timer) { clearTimeout(search_timer); } data = $.param({ filters_active: (active_inp.val() == 1) ? "yes" : "no", filters_expired: (expired_inp.val() == 1) ? "yes" : "no" }); if (search_query.val() != '') { data += '&' + $.param({ q: search_query.val(), }); } var duration = 200; search_timer = setTimeout(function(){ search_query.addClass("onprogress"); $.ajax({ url: $form.attr("action"), type: $form.attr("method"), dataType: 'html', data: data, complete: function() { search_query.removeClass('onprogress'); }, success: function(resp) { $resp = $(resp); if ($resp.find(".skeleton-aside .js-search-results").length == 1) { $(".skeleton-aside .js-search-results") .html($resp.find(".skeleton-aside .js-search-results").html()); NextPost.Tooltip(); } } }); }, duration); }); /** * Online users filtration */ var search_timer; var search_xhr; var $form = $(".skeleton-aside .search-box"); $("body").on("click", "a.js-filter-online", function() { var online_btn = $(".js-filter-online"); var online_inp = $form.find(":input[name='filters_online']"); var search_query = $form.find(":input[name='q']"); if (online_inp.val() == 0) { online_inp.val(1); online_btn.removeClass('button--light-outline'); online_btn.addClass('active'); } else { online_inp.val(0) online_btn.addClass('button--light-outline'); online_btn.removeClass('active'); } if (search_xhr) { // Abort previous ajax request search_xhr.abort(); } if (search_timer) { clearTimeout(search_timer); } data = $.param({ filters_online: (online_inp.val() == 1) ? "yes" : "no" }); if (search_query.val() != '') { data += '&' + $.param({ q: search_query.val(), }); } var duration = 200; search_timer = setTimeout(function(){ search_query.addClass("onprogress"); $.ajax({ url: $form.attr("action"), type: $form.attr("method"), dataType: 'html', data: data, complete: function() { search_query.removeClass('onprogress'); }, success: function(resp) { $resp = $(resp); if ($resp.find(".skeleton-aside .js-search-results").length == 1) { $(".skeleton-aside .js-search-results") .html($resp.find(".skeleton-aside .js-search-results").html()); NextPost.Tooltip(); } } }); }, duration); }); /** * Recently online users filtration */ var search_timer; var search_xhr; var $form = $(".skeleton-aside .search-box"); $("body").on("click", "a.js-filter-recently", function() { var recently_btn = $(".js-filter-recently"); var recently_inp = $form.find(":input[name='filters_recently']"); var search_query = $form.find(":input[name='q']"); if (recently_inp.val() == 0) { recently_inp.val(1); recently_btn.removeClass('button--light-outline'); recently_btn.addClass('active'); } else { recently_inp.val(0) recently_btn.addClass('button--light-outline'); recently_btn.removeClass('active'); } if (search_xhr) { // Abort previous ajax request search_xhr.abort(); } if (search_timer) { clearTimeout(search_timer); } data = $.param({ filters_recently: (recently_inp.val() == 1) ? "yes" : "no" }); if (search_query.val() != '') { data += '&' + $.param({ q: search_query.val(), }); } var duration = 200; search_timer = setTimeout(function(){ search_query.addClass("onprogress"); $.ajax({ url: $form.attr("action"), type: $form.attr("method"), dataType: 'html', data: data, complete: function() { search_query.removeClass('onprogress'); }, success: function(resp) { $resp = $(resp); if ($resp.find(".skeleton-aside .js-search-results").length == 1) { $(".skeleton-aside .js-search-results") .html($resp.find(".skeleton-aside .js-search-results").html()); NextPost.Tooltip(); } } }); }, duration); }); }
Step 12
Open file /assets/js/core.js and find function NextPost.Tooltip. Replace this function with new one:
/** * ToolTips */ NextPost.Tooltip = function() { $(".tippy").each(function() { var dom = $(this)[0]; if ($(this).hasClass("js-tooltip-ready")) { var tip = $(this).data("tip"); var popper = tip.getPopperElement(dom); if (popper.length) { tip.update(popper); } } else { var tip = Tippy(dom); $(this).addClass("js-tooltip-ready"); $(this).data("tip", tip); } }); }
That’s all, thank you!
Now clean browser cache and take a look at your changes. For example I attached screenshot from Chrome.
