-3

I'm looking for how to tracking user activity (Example: XXX commented on the post/ XXX signed in/XXX shared a post or etc; XXX are registered User/IP address) on my Codeigniter website.

Is it have any recommendations for doing this function because I already researched a few days and get no idea.

IT OA
  • 11
  • 1
  • 3

1 Answers1

1

Normally I would not even answer a question like this as it is too broad but since I have a similar system I will share my approach. Please note, I am offering no support on this, but rather this is for you to get an idea and/or develop your own implementation.

Let's assume we want to log user logins/failures:

    if ($this->ion_auth->login($identity, $this->input->post('password'), false)) {
        $this->curr_user->update_ip();
        $this->activity->log('logged_in');
        $this->response->success()->json();
    } else {
        $this->activity->log('login_failed', array('username' => $identity));
        $this->response->error($this->ion_auth->errors())->json();
    }

We simply call the log function before redirecting/rendering output but after the desired action (insert/update, login, .etc.) is completed.

MODEL

The log function uses a switch statement to keep messages homogeneous, and keeps certain information separate from the main message for querying purposes. To be honest using a switch statement isn't super elegant here especially if you have a lot of messages, so implementing a table to hold the messages wouldn't be a bad idea, but this works for my needs.

get_logs is a cool little function that combines all the data and renders it in a sorted list by date the action occurred.

class User_activity_model extends CI_Model {

    /**
     * Admin-only viewable log types
     * 
     * @var array
     */
    private $admin_types = array('page_access', 'user_registered', 'logged_in', 'login_failed', 'login_failed_sa', 'delete');

    /**
     * Main user activity logging function
     * 
     * @param string $action
     * @param array $arr Additional attributes per case
     * @return void
     */
    public function log($action, $arr = array()) {
        switch ($action) {
            default:
                return;
            case 'backup_created':
                $msg = "{username} created a backup {$arr['filename']} at {time}.";
                break;
            case 'backup_restored':
                $msg = "{username} restored backup {$arr['filename']} at {time}.";
                break;
            case 'user_registered':
                $msg = "{$arr['first_name']} {$arr['last_name']} registered with username: {$arr['username']}.";
                break;
            case 'added':
                $msg = "{username} added item {$arr['id']} to the {$arr['table']} table.";
                break;
            case 'modified':
                $msg = "{username} modified item {$arr['id']} in the {$arr['table']} table.";
                break;
            case 'deleted':
                $msg = "{username} deleted item {$arr['id']} from the {$arr['table']} table.";
                break;
            case 'published':
                $msg = "{username} published item {$arr['id']} from the {$arr['table']} table.";
                break;
            case 'unpublished':
                $msg = "{username} unpublished item {$arr['id']} from the {$arr['table']} table.";
                break;
            case 'logged_in':
                $ip = $this->input->ip_address();
                $msg = "{username} logged in at {time} from IP {$ip}.";
                break;
            case 'login_failed':
                $ip = $this->input->ip_address();
                $msg = "Someone tried to login with username {$arr['username']} at {time} from IP {$ip}.";
                break;
            case 'login_failed_sa':
                $ip = $this->input->ip_address();
                $msg = "Someone tried to login with social auth provider {$arr['provider']} at {time} from IP {$ip}.";
                break;
            case 'page_access':
                $identity = $this->ion_auth->logged_in() ? '{username}' : "Someone (IP: {$this->input->ip_address()})";
                $msg = "{$identity} tried to access a page they didn't have permission for: {$arr['uri']}.";
                break;
            case 'logout':
                $msg = "{username} logged out at {time}.";
                break;
            case 'user_forgotten':
                $msg = 'Someone deleted their account.';
                break;
        }
        $this->add($action, $msg);
    }

    /**
     * Adds identifier information to the insert query
     * 
     * @param string $action
     * @param string $msg
     * @return void
     */
    private function add($action, $msg) {
        $data = array(
            'ip' => $this->input->ip_address(),
            'action' => $action,
            'type' => in_array($action, $this->admin_types) ? 'admin' : 'all',
            'message' => $msg
        );
        if ($this->ion_auth->logged_in()) {
            $data['username'] = $this->curr_user->details()->username;
            $data['user_id'] = $this->curr_user->id();
        }
        $this->db->set('time', 'NOW()', false);
        $this->db->insert('user_activity', $data);
    }

    /**
     * Generates log array
     * 
     * Format:
     * 
     * array(
     *       [date]
     *             array(
     *                   [0] = message1
     *                   [1] = message2
     * 
     * @param boolean $admin_only Show all logs?
     * @return boolean|object Logs object, FALSE otherwise
     */
    public function get_logs($admin_only = true) {
        if (!$admin_only) {
            $this->db->where('type !=', 'admin');
        }
        $this->db->limit(10000);
        $this->db->order_by('time', 'DESC');
        $query = $this->db->get('user_activity');
        if ($query->num_rows() < 1) {
            return false;
        }
        $rows = $query->result();
        $data = new \stdClass();
        foreach ($rows as $row) {
            // replace {time} with timezone converted time
            $time = $this->timezone->convert($row->time);
            $row->message = str_replace('{time}', $time, $row->message);
            $username = is_null($row->username) ? $this->lang->line('deleted_user') : $row->username;
            $row->message = str_replace('{username}', $username, $row->message);
            // date Y-m-d acts as key for messages on that day
            $key = date('Y-m-d', strtotime($time));
            $data->{$key}[] = $row;
        }
        return $data;
    }

    /**
     * Truncates user_activity table
     * 
     * @return boolean
     */
    public function delete_logs() {
        return $this->db->truncate('user_activity');
    }

    /**
     * Stub for automate hook to add 'added' activity
     * 
     * @param array $data
     */
    public function automate_activity_add($data) {
        $this->activity->log('added', array('table' => $data['table'], 'id' => $data['id']));
    }

    /**
     * Stub for automate hook to add 'modified' activity
     * 
     * @param array $data
     */
    public function automate_activity_modify($data) {
        $this->activity->log('modified', array('table' => $data['table'], 'id' => $data['id']));
    }

}

DATABASE

enter image description here

db dump: https://pastebin.com/wCEnUigH

Alex
  • 9,215
  • 8
  • 39
  • 82