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.

This image has an empty alt attribute; its file name is image-1024x261.png