Archived
1
0
Fork 0

Merge branch '11-logg-user-actions' into 'master'

Resolve "Logg user actions."

Closes #11

See merge request pnx/httpcb!8
This commit is contained in:
Henrik Hautakoski 2018-04-05 15:39:51 +00:00
commit a3ac12bc2d
9 changed files with 324 additions and 4 deletions

View file

@ -30,7 +30,8 @@ use Httpcb\Auth,
Httpcb\Menu;
use App\Listener\AclListener,
App\Listener\DispatchListener;
App\Listener\DispatchListener,
App\Listener\ActivityLog;
/**
* The FactoryDefault Dependency Injector automatically register the right services providing a full stack framework
@ -216,6 +217,17 @@ $di->setShared('db', function () use ($di, $config) {
return $db;
});
$di->setShared('eventsManager', function () {
$activityLog = new ActivityLog();
$eventsManager = new Phalcon\Events\Manager();
$eventsManager->attach('user', $activityLog);
$eventsManager->attach('auth', $activityLog);
return $eventsManager;
});
/**
* If the configuration specify the use of metadata adapter use it or use memory otherwise
*/
@ -286,8 +298,11 @@ $di->set('oauth', function() use ($config) {
return new OAuth($config);
});
$di->set('auth', function() use ($config) {
return new Auth($config);
$di->set('auth', function() use ($di, $config) {
$auth = new Auth($config);
$auth->setEventsManager($di->get('eventsManager'));
return $auth;
});
$di->set('acl', 'Httpcb\Acl');

View file

@ -3,7 +3,8 @@
namespace App\Controller;
use App\Controller\ControllerBase,
App\Form\UserSettings as UserSettingsForm;
App\Form\UserSettings as UserSettingsForm,
App\Model\Data\ActivityLog;
class UserController extends ControllerBase
{
@ -34,4 +35,14 @@ class UserController extends ControllerBase
$this->view->form = $form;
}
public function activityAction($page = 1)
{
$user = $this->_getAuth()->getUser();
$paginator = ActivityLog::getPaginationList($user->getId(), $page);
$this->view->page = $paginator->getPaginate();
$this->view->pagination_url = '/user/activity/';
}
}

View file

@ -28,7 +28,11 @@ class Auth extends Component
// Verify password
$hash = $user->getPassword();
if (strlen($hash) > 1 && password_verify($password, $hash)) {
$this->setIdentity($user->getId());
$this->_eventsManager->fire('auth:onLogin', $this, 'password');
return true;
}
}
@ -68,6 +72,9 @@ class Auth extends Component
}
$this->setIdentity($user->getId());
$this->_eventsManager->fire('auth:onLogin', $this,
"OAuth {$auth['provider']}");
}
/**

View file

@ -0,0 +1,68 @@
<?php
namespace App\Listener;
use Phalcon\Mvc\User\Plugin,
Phalcon\Events\Event,
App\Model\Data\User,
App\Model\Data\ActivityLog as ActivityLogger,
Httpcb\Auth;
class ActivityLog extends Plugin
{
/**
* @var ActivityLogger
*/
protected $_table = null;
/**
* On login event.
*
* @param Event $event
* @param Auth $auth
* @param $type
*/
public function onLogin(Event $event, Auth $auth, $type)
{
$this->_log($auth->getUser(), "Logged in ({$type})");
}
/**
* @param Event $event
* @param User $auth
*/
public function onPasswordCreated(Event $event, User $user)
{
$this->_log($user, "Created password");
}
/**
* @param Event $event
* @param User $auth
*/
public function onPasswordChanged(Event $event, User $user)
{
$this->_log($user, "Changed password");
}
protected function _log(User $user, $message)
{
$ip = (new \Phalcon\Http\Request())->getClientAddress();
$data = [
'user_id' => $user->getId(),
'ip' => $ip,
'message' => $message
];
return $this->_getLogger()->create($data);
}
protected function _getLogger()
{
if ($this->_table === null) {
$this->_table = new ActivityLogger();
}
return $this->_table;
}
}

View file

@ -0,0 +1,32 @@
<?php
use Phinx\Migration\AbstractMigration;
class CreateActivityLog extends AbstractMigration
{
public function up()
{
$table = $this->table('activity_log');
$table->addColumn('timestamp', 'datetime', [
'default' => 'CURRENT_TIMESTAMP',
'after' => 'email'
]);
$table->addColumn('user_id', 'integer')
->addForeignKey('user_id', 'user', ['id']);
$table->addColumn('ip', 'string', [
'null' => true,
'length' => 50,
]);
$table->addColumn('message', 'string', [
'null' => true,
'length' => 255,
]);
$table->save();
}
}

View file

@ -0,0 +1,131 @@
<?php
namespace App\Model\Data;
use \Phalcon\Paginator\Adapter\QueryBuilder;
class ActivityLog extends Base
{
protected $id;
protected $timestamp;
protected $user_id;
protected $ip;
protected $message;
/**
* @return mixed
*/
public function getId()
{
return $this->id;
}
/**
* @param mixed $id
* @return User
*/
public function setId($id)
{
$this->id = $id;
return $this;
}
/**
* @return mixed
*/
public function getTimestamp()
{
return $this->timestamp;
}
/**
* @param mixed $timestamp
* @return ActivityLog
*/
public function setTimestamp($timestamp)
{
$this->timestamp = $timestamp;
return $this;
}
/**
* @return mixed
*/
public function getUserId()
{
return $this->user_id;
}
/**
* @param mixed $user_id
* @return ActivityLog
*/
public function setUserId($user_id)
{
$this->user_id = $user_id;
return $this;
}
/**
* @return mixed
*/
public function getIp()
{
return $this->ip;
}
/**
* @param mixed $ip
* @return ActivityLog
*/
public function setIp($ip)
{
$this->ip = $ip;
return $this;
}
/**
* @return mixed
*/
public function getMessage()
{
return $this->message;
}
/**
* @param mixed $message
* @return ActivityLog
*/
public function setMessage($message)
{
$this->message = $message;
return $this;
}
/**
* @param $userid
* @param int $page
* @param int $limit
* @return \Phalcon\Paginator\AdapterInterface
*/
public static function getPaginationList($userid, $page = 1, $limit = 30)
{
$builder = (new self())->getModelsManager()->createBuilder();
$builder->from(self::class)
->where('user_id = :uid:', array('uid' => $userid))
->orderBy('timestamp DESC');
$paginator = new QueryBuilder(array(
'builder' => $builder,
'page' => $page,
'limit' => $limit
));
return $paginator;
}
}

View file

@ -30,6 +30,12 @@ class User extends Model
public function initialize()
{
$this->useDynamicUpdate(true);
// Keep snapshots so we know if something changes.
$this->keepSnapshots(true);
// Set default event manager
$this->setEventsManager($this->getDI()->get('eventsManager'));
}
/**
@ -200,4 +206,22 @@ class User extends Model
"bind" => [ 'v' => $value ]
]);
}
public function beforeSave()
{
$manager = $this->getEventsManager();
// EventManager exist and password field has changed.
if ($manager && $this->hasChanged('password')) {
$old_value = $this->getOldSnapshotData()['password'];
// Empty password before
if (strlen($old_value) < 1) {
$manager->fire('user:onPasswordCreated', $this);
} else {
$manager->fire('user:onPasswordChanged', $this);
}
}
}
}

View file

@ -16,6 +16,7 @@
<ul aria-labelledby="user-dropdown" class="navigation-user-menu-dropdown-list">
<li>{{ link_to(['for': 'user-settings'], '<i class="icon ion-gear-a"></i> Settings') }}</li>
<li>{{ link_to('/user/activity', '<i class="icon ion-android-list"></i> Activity') }}</li>
<li role="separator" class="divider"></li>
<li>{{ link_to(['for': 'logout'], '<i class="icon ion-log-out"></i> Log out') }}</li>
</ul>

View file

@ -0,0 +1,31 @@
<div class="section">
<h3>Activity Log</h3>
<table class="table table-condensed table-striped table-hover">
<thead>
<tr>
<th>Date</th>
<th>Ip</th>
<th>Message</th>
</tr>
</thead>
<tbody>
{% for item in page.items %}
<tr>
<td>{{ item.getTimestamp() }}</td>
<td>{{ item.getIp() }}</td>
<td>{{ item.getMessage() }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<nav class="text-center" aria-label="Page navigation">
{{ partial('pagination') }}
</nav>
</div>