Archived
1
0
Fork 0

Compare commits

..

No commits in common. "dev" and "v1.1" have entirely different histories.
dev ... v1.1

53 changed files with 193 additions and 398 deletions

View file

@ -30,7 +30,7 @@ session:
statsKey: _httpcb_sess_idx statsKey: _httpcb_sess_idx
prefix: _httpcb_sess_ prefix: _httpcb_sess_
#sendgrid: #sendgrid
#key: value #key: value
# OAuth # OAuth

View file

@ -4,61 +4,58 @@ router:
routes: routes:
home-route: home-route:
pattern: '/' pattern: '/'
path: Index::index path:
controller: index
action: index
about-route: about-route:
pattern: '/about' pattern: '/about'
path: Index::about path:
controller: index
# Callbacks action: about
cb-list:
pattern: '/callback/list'
path: Callback::list
cb-new:
pattern: '/callback/new'
path: Callback::new
cb-created: cb-created:
pattern: '/callback/created/{id}' pattern: '/callback/created/{id}'
path: Callback::created path:
cb-show: controller: callback
pattern: '/callback/show/{id}' action: created
path: Callback::show
cb-endpoint: cb-endpoint:
pattern: '/cb/{id}/:params' pattern: '/cb/{id}/:params'
path: Api::endpoint path:
controller: api
# login action: endpoint
login: login:
pattern: '/login' pattern: '/login'
path: Auth::index path:
controller: auth
action: index
logout: logout:
pattern: '/logout' pattern: '/logout'
path: Auth::logout path:
controller: auth
action: logout
oauth: oauth:
pattern: '/login/{strategy:([a-z]+)}/:params' pattern: '/login/{strategy:([a-z]+)}/:params'
path: Auth::oauth path:
controller: auth
action: oauth
oauth-disconnect: oauth-disconnect:
pattern: '/oauth/{provider:([a-z]+)}/disconnect' pattern: '/oauth/{provider:([a-z]+)}/disconnect'
path: 'User::oauthdisconnect' path: 'User::oauthdisconnect'
oauth-disconnect-confirm: oauth-disconnect-confirm:
pattern: '/oauth/{provider:([a-z]+)}/disconnect/{confirm}' pattern: '/oauth/{provider:([a-z]+)}/disconnect/{confirm}'
path: 'User::oauthdisconnect' path: 'User::oauthdisconnect'
# User
user-register: user-register:
pattern: '/register' pattern: '/register'
path: Auth::register path: Auth::register
user-settings: user-settings:
pattern: '/settings' pattern: '/settings'
path: User::settings path:
user-activity-log: controller: user
pattern: '/user/activity' action: settings
path: User::activity
activation-link: activation-link:
pattern: '/activate/{link}' pattern: '/activate/{link}'
path: Api::activationlink path:
controller: api
action: activationlink
# Backend # Backend
backend-home: backend-home:
@ -73,15 +70,12 @@ router:
backend-user-edit: backend-user-edit:
pattern: '/admin/user/{id:([0-9]+)}' pattern: '/admin/user/{id:([0-9]+)}'
path: backend::user::edit path: backend::user::edit
backend-user-impersonate:
pattern: '/admin/impersonate/{id:([0-9]+)}'
path: backend::user::impersonate
backend-user-activation-email:
pattern: '/admin/user/{id:([0-9]+)}/activation'
path: backend::user::activation-email
backend-user-status: backend-user-status:
pattern: '/admin/user/{id:([0-9]+)}/status/{type}' pattern: '/admin/user/{id:([0-9]+)}/{type}'
path: backend::user::status path:
module: backend
controller: user
action: status
backend-log: backend-log:
pattern: '/admin/log{page:/?([0-9]+)?}' pattern: '/admin/log{page:/?([0-9]+)?}'
path: backend::log::index path: backend::log::index

View file

@ -6,7 +6,6 @@ use App\Controller\ControllerBase,
App\Model\Data\Callback as CallbackModel, App\Model\Data\Callback as CallbackModel,
App\Model\Data\Request as RequestModel, App\Model\Data\Request as RequestModel,
App\Model\Data\RequestMeta as RequestMetaModel, App\Model\Data\RequestMeta as RequestMetaModel,
App\Model\Data\User,
App\Model\Data\UserActivation; App\Model\Data\UserActivation;
class ApiController extends ControllerBase class ApiController extends ControllerBase

View file

@ -118,11 +118,8 @@ class AuthController extends ControllerBase
} }
$user = new User(); $user = new User();
$user->assign( $user->assign($data->toArray(), null,
$data->toArray(), [ 'email', 'username', 'firstname', 'lastname' ]);
null,
['email', 'username', 'firstname', 'lastname']
);
$form = new RegistrationForm($user); $form = new RegistrationForm($user);

View file

@ -55,8 +55,7 @@ class CallbackController extends ControllerBase
return $this->response->redirect(array( return $this->response->redirect(array(
'for' => 'cb-created', 'for' => 'cb-created',
'id' => $callback->getPublicId() 'id' => $callback->getPublicId()));
));
} else { } else {
foreach($callback->getMessages() as $msg) { foreach($callback->getMessages() as $msg) {
$this->flash->error($msg); $this->flash->error($msg);
@ -72,6 +71,8 @@ class CallbackController extends ControllerBase
$msg .= '</ul>'; $msg .= '</ul>';
$this->flash->message('error', $msg); $this->flash->message('error', $msg);
} }
} }
$this->view->form = $form; $this->view->form = $form;
@ -84,6 +85,7 @@ class CallbackController extends ControllerBase
{ {
$row = CallbackModel::get($id); $row = CallbackModel::get($id);
if (!$row) { if (!$row) {
} }
$this->view->id = $id; $this->view->id = $id;
} }

View file

@ -18,7 +18,6 @@ class ControllerBase extends Controller
protected function _forward404() protected function _forward404()
{ {
$this->dispatcher->forward(array( $this->dispatcher->forward(array(
'namespace' => 'App\\Controller',
'controller' => 'error', 'controller' => 'error',
'action' => 'show404' 'action' => 'show404'
)); ));

View file

@ -15,3 +15,4 @@ class IndexController extends ControllerBase
{ {
} }
} }

View file

@ -44,11 +44,8 @@ class UserController extends ControllerBase
]); ]);
// Send the email. // Send the email.
$this->di->getMail()->send( $this->di->getMail()->send('Httpcb password activation',
'Httpcb password activation', $user->getEmail(), $content);
$user->getEmail(),
$content
);
$msg = "For security reasons. Before a password can be created " $msg = "For security reasons. Before a password can be created "
. "a email has been sent to <strong>{$user->getEmail()}</strong> with " . "a email has been sent to <strong>{$user->getEmail()}</strong> with "

View file

@ -97,33 +97,4 @@ class UserController extends \Phalcon\Mvc\Controller
$this->flash->success('The account was: ' . $status); $this->flash->success('The account was: ' . $status);
$this->response->redirect('/admin'); $this->response->redirect('/admin');
} }
public function activationEmailAction($id)
{
$user = User::findFirstById($id);
if ($user) {
if ($user->isSuspended()) {
$this->eventsManager->fire('auth:onSentActivation', $user);
$this->flash->success('Activation email sent to: ' . $user->email);
} else {
$this->flash->error('Only suspended users can be sent activation emails.');
}
} else {
$this->flash->error('Invalid user: ' . $id);
}
$this->response->redirect('/admin');
}
public function impersonateAction($id)
{
$user = User::findFirstById($id);
try {
$this->auth->impersonate($user);
$this->response->redirect('/');
} catch (\Exception $ex) {
$this->flash->error($ex->getMessage());
$this->response->redirect('/admin');
}
}
} }

View file

@ -7,14 +7,12 @@ use Phalcon\Forms\Form;
/** /**
* Element types * Element types
*/ */
use Phalcon\Forms\Element\Text; use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Submit; use Phalcon\Forms\Element\Submit;
/** /**
* Validators * Validators
*/ */
use Phalcon\Validation\Validator\StringLength; use Phalcon\Validation\Validator\StringLength;
class CallbackCreate extends Form class CallbackCreate extends Form

View file

@ -7,7 +7,6 @@ use Phalcon\Forms\Form;
/** /**
* Element types * Element types
*/ */
use Phalcon\Forms\Element\Text; use Phalcon\Forms\Element\Text;
use Phalcon\Forms\Element\Password; use Phalcon\Forms\Element\Password;
use Phalcon\Forms\Element\Submit; use Phalcon\Forms\Element\Submit;
@ -15,7 +14,6 @@ use Phalcon\Forms\Element\Submit;
/** /**
* Validators * Validators
*/ */
use Phalcon\Validation\Validator\PresenceOf; use Phalcon\Validation\Validator\PresenceOf;
use Phalcon\Validation\Validator\Email as EmailValidator; use Phalcon\Validation\Validator\Email as EmailValidator;
use Phalcon\Validation\Validator\StringLength; use Phalcon\Validation\Validator\StringLength;

View file

@ -5,19 +5,16 @@ namespace App\Form;
/** /**
* Models * Models
*/ */
use App\Model\Data\User; use App\Model\Data\User;
/** /**
* Phalcon Form * Phalcon Form
*/ */
use Httpcb\Form as FormBase; use Httpcb\Form as FormBase;
/** /**
* Element types * Element types
*/ */
use Phalcon\Forms\Element\Text, use Phalcon\Forms\Element\Text,
Phalcon\Forms\Element\Password, Phalcon\Forms\Element\Password,
Phalcon\Forms\Element\Submit; Phalcon\Forms\Element\Submit;
@ -25,7 +22,6 @@ use Phalcon\Forms\Element\Text,
/** /**
* Validators * Validators
*/ */
use Phalcon\Validation, use Phalcon\Validation,
Phalcon\Validation\Validator\Callback as CallbackValidator, Phalcon\Validation\Validator\Callback as CallbackValidator,
Phalcon\Validation\Validator\Alnum as AlnumValidator, Phalcon\Validation\Validator\Alnum as AlnumValidator,
@ -58,9 +54,7 @@ class Registration extends FormBase
'messageMinimum' => 'Username must be at least :min characters long.', 'messageMinimum' => 'Username must be at least :min characters long.',
]), ]),
new CallbackValidator([ new CallbackValidator([
'callback' => function ($data) { 'callback' => function($data) { return User::findFirstByUsername($data['username']) === false; },
return User::findFirstByUsername($data['username']) === false;
},
'message' => 'The username already exists.', 'message' => 'The username already exists.',
'attribute' => 'username', 'attribute' => 'username',
]) ])
@ -93,9 +87,7 @@ class Registration extends FormBase
$email->addValidators([ $email->addValidators([
new CallbackValidator([ new CallbackValidator([
'callback' => function ($data) { 'callback' => function($data) { return User::findFirstByEmail($data['email']) === false; },
return User::findFirstByEmail($data['email']) === false;
},
'message' => 'This email already exist.', 'message' => 'This email already exist.',
]) ])
]); ]);

View file

@ -5,19 +5,16 @@ namespace App\Form;
/** /**
* Models * Models
*/ */
use App\Model\Data\User as UserModel; use App\Model\Data\User as UserModel;
/** /**
* Form * Form
*/ */
use Httpcb\Form as FormBase; use Httpcb\Form as FormBase;
/** /**
* Element types * Element types
*/ */
use Phalcon\Forms\Element\Text, use Phalcon\Forms\Element\Text,
Phalcon\Forms\Element\Password, Phalcon\Forms\Element\Password,
Phalcon\Forms\Element\Submit; Phalcon\Forms\Element\Submit;
@ -25,7 +22,6 @@ use Phalcon\Forms\Element\Text,
/** /**
* Validators * Validators
*/ */
use Phalcon\Validation\Validator\Callback as CallbackValidator, use Phalcon\Validation\Validator\Callback as CallbackValidator,
Phalcon\Validation\Validator\Uniqueness as UniquenessValidator, Phalcon\Validation\Validator\Uniqueness as UniquenessValidator,
Phalcon\Validation\Validator\Alnum as AlnumValidator, Phalcon\Validation\Validator\Alnum as AlnumValidator,

View file

@ -70,6 +70,7 @@ class Acl
if ($def instanceof Config) { if ($def instanceof Config) {
$inherits = $def->get('inherits'); $inherits = $def->get('inherits');
$description = $def->get('description'); $description = $def->get('description');
} }
$role = new Role($name, $description); $role = new Role($name, $description);

View file

@ -10,7 +10,6 @@ use App\Model\Data\User,
class Auth extends Injectable class Auth extends Injectable
{ {
const SESSION_KEY = 'auth'; const SESSION_KEY = 'auth';
const IMPERSONATOR_ID = 'auth.impersonator';
/** /**
* Login using email/user + password combination. * Login using email/user + password combination.
@ -64,11 +63,8 @@ class Auth extends Injectable
$this->setIdentity($user->getId()); $this->setIdentity($user->getId());
$this->eventsManager->fire( $this->eventsManager->fire('auth:onLogin', $this,
'auth:onLogin', "OAuth {$data->getProvider()}");
$this,
"OAuth {$data->getProvider()}"
);
return new Result(Result::SUCCESS); return new Result(Result::SUCCESS);
@ -87,40 +83,6 @@ class Auth extends Injectable
$this->eventsManager->fire('auth:onLogin', $this, 'System'); $this->eventsManager->fire('auth:onLogin', $this, 'System');
} }
public function getImpersonator()
{
$id = $this->session->get(self::IMPERSONATOR_ID);
return $id !== null ? User::findFirst($id) : null;
}
/**
* Impersonate a user
*
* @param User $user
*/
public function impersonate(User $user)
{
$current = $this->getIdentity();
if ($current === null) {
throw new \InvalidArgumentException("Need to be authenticated to be able to impersonate someone");
}
if ($current->getId() === $user->getId()) {
// Same user
throw new \DomainException("Can't impersonate yourself");
}
$this->session->set(self::IMPERSONATOR_ID, $current->getId());
$this->setIdentity($user->getId());
$this->eventsManager->fire('auth:onImpersonate', $this, $current);
}
public function impersonateClear($imp_id)
{
$this->session->remove(self::IMPERSONATOR_ID);
$this->session->set(self::SESSION_KEY, $imp_id);
}
/** /**
* @param $identity * @param $identity
* @return Auth * @return Auth
@ -170,12 +132,7 @@ class Auth extends Injectable
*/ */
public function clearIdentity() public function clearIdentity()
{ {
$imp_id = $this->session->get(self::IMPERSONATOR_ID);
if ($imp_id !== null) {
$this->impersonateClear($imp_id);
} else {
$this->session->remove(self::SESSION_KEY); $this->session->remove(self::SESSION_KEY);
}
return $this; return $this;
} }
} }

View file

@ -2,8 +2,7 @@
namespace Httpcb; namespace Httpcb;
class Debug class Debug {
{
public static function dump($var, $label = null, $echo = true) public static function dump($var, $label = null, $echo = true)
{ {

View file

@ -53,10 +53,7 @@ class Form extends FormBase
$xhtml .= sprintf( $xhtml .= sprintf(
'<label class="%s" for="%s">%s</label>', '<label class="%s" for="%s">%s</label>',
$opt['label-class'], $opt['label-class'], $ele->getName(), $ele->getLabel());
$ele->getName(),
$ele->getLabel()
);
} }
$xhtml .= '<div class="' . implode(' ', $opt['class']) . '">' $xhtml .= '<div class="' . implode(' ', $opt['class']) . '">'

View file

@ -126,22 +126,15 @@ class Menu extends Tag
return $xhtml; return $xhtml;
} }
$xhtml = self::tagHtml( $xhtml = self::tagHtml('li', $node->isActive()
'li',
$node->isActive()
? array('class' => $this->_activeClass) : null, ? array('class' => $this->_activeClass) : null,
false, false, false, true);
false,
true
);
// Generate the link. // Generate the link.
$xhtml .= self::linkTo($node->getHref(), $node->getCaption()); $xhtml .= self::linkTo($node->getHref(), $node->getCaption());
if ( if ($node->isActive() && $node->hasChildren()
$node->isActive() && $node->hasChildren() && ($max_depth === null || $depth < $max_depth)) {
&& ($max_depth === null || $depth < $max_depth)
) {
$xhtml .= $this->_renderMenu($node->getChildren(), $depth + 1, $max_depth); $xhtml .= $this->_renderMenu($node->getChildren(), $depth + 1, $max_depth);
} }

View file

@ -81,3 +81,4 @@ class Container
return $this; return $this;
} }
} }

View file

@ -16,8 +16,6 @@ use Phalcon\Di\FactoryDefault as DiDefault,
Phalcon\Cache\Frontend\Data as FrontendDataCache, Phalcon\Cache\Frontend\Data as FrontendDataCache,
Phalcon\Cache\Backend\Apc as BackendApcCache, Phalcon\Cache\Backend\Apc as BackendApcCache,
Phalcon\Translate\Adapter\NativeArray as TranslateAdapter, Phalcon\Translate\Adapter\NativeArray as TranslateAdapter,
Phalcon\Storage\AdapterFactory as StorageAdapterFactory,
Phalcon\Cache\AdapterFactory as CacheAdapterFactory,
Phalcon\Logger, Phalcon\Logger,
Phalcon\Mvc\Router; Phalcon\Mvc\Router;
@ -32,8 +30,7 @@ use Httpcb\Auth,
use App\Listener\AccessListener, use App\Listener\AccessListener,
App\Listener\DispatchListener, App\Listener\DispatchListener,
App\Listener\ActivityLog, App\Listener\ActivityLog;
App\Listener\AuthEmailListener;
class Services extends DiDefault class Services extends DiDefault
{ {
@ -49,7 +46,8 @@ class Services extends DiDefault
if (substr($method->getName(),0, 11) == '_initShared') { if (substr($method->getName(),0, 11) == '_initShared') {
$service = lcfirst(substr($method->getName(), 11)); $service = lcfirst(substr($method->getName(), 11));
$this->setShared($service, $method->getClosure($this)); $this->setShared($service, $method->getClosure($this));
} else if (substr($method->getName(), 0, 5) == '_init') { }
else if (substr($method->getName(),0, 5) == '_init') {
$service = lcfirst(substr($method->getName(), 5)); $service = lcfirst(substr($method->getName(), 5));
$this->set($service, $method->getClosure($this)); $this->set($service, $method->getClosure($this));
} }
@ -179,11 +177,8 @@ class Services extends DiDefault
$options = $mdConfig['options']; $options = $mdConfig['options'];
$adapter = $mdConfig['adapter']; $adapter = $mdConfig['adapter'];
$serializerFactory = new \Phalcon\Storage\SerializerFactory();
$factory = new CacheAdapterFactory($serializerFactory);
$class = 'Phalcon\Mvc\Model\MetaData\\' . $adapter; $class = 'Phalcon\Mvc\Model\MetaData\\' . $adapter;
return new $class($factory, $options); return new $class($options);
} }
// Otherwise, default to Memory. // Otherwise, default to Memory.
@ -200,11 +195,8 @@ class Services extends DiDefault
$adapter_name = isset($data['adapter']) ? $data['adapter'] : 'Stream'; $adapter_name = isset($data['adapter']) ? $data['adapter'] : 'Stream';
$options = $data['options']; $options = $data['options'];
$serializerFactory = new \Phalcon\Storage\SerializerFactory();
$factory = new StorageAdapterFactory($serializerFactory);
$class = 'Phalcon\Session\Adapter\\' . $adapter_name; $class = 'Phalcon\Session\Adapter\\' . $adapter_name;
$adapter = new $class($factory, $options); $adapter = new $class($options);
} }
// Default to Stream // Default to Stream
else { else {
@ -318,7 +310,7 @@ class Services extends DiDefault
$config = $this->get('config')->router; $config = $this->get('config')->router;
// Create the router // Create the router
$router = new Router(false); $router = new Router();
$router->removeExtraSlashes($config->get('removeExtraSlashes', false)); $router->removeExtraSlashes($config->get('removeExtraSlashes', false));
foreach($config->routes as $name => $def) { foreach($config->routes as $name => $def) {
@ -335,9 +327,6 @@ class Services extends DiDefault
$router->add($def->get('pattern'), $path) $router->add($def->get('pattern'), $path)
->setName($name); ->setName($name);
} }
$router->notFound(['controller' => 'error', 'action' => 'show404']);
return $router; return $router;
} }
@ -348,7 +337,6 @@ class Services extends DiDefault
$eventsManager = new \Phalcon\Events\Manager(); $eventsManager = new \Phalcon\Events\Manager();
$eventsManager->attach('user', $activityLog); $eventsManager->attach('user', $activityLog);
$eventsManager->attach('auth', $activityLog); $eventsManager->attach('auth', $activityLog);
$eventsManager->attach('auth', new AuthEmailListener);
return $eventsManager; return $eventsManager;
} }

View file

@ -33,8 +33,7 @@ class ServerUrl extends AbstractHelper
// remove port if it's the default port. // remove port if it's the default port.
if (($scheme == 'http' && $port == 80) if (($scheme == 'http' && $port == 80)
|| ($scheme == 'https' && $port == 443) || ($scheme == 'https' && $port == 443)) {
) {
$port = null; $port = null;
} }

View file

@ -23,19 +23,6 @@ class ActivityLog extends Injectable
$this->_log($auth->getUser(), sprintf("Logged in (%s)", $type)); $this->_log($auth->getUser(), sprintf("Logged in (%s)", $type));
} }
/**
* On Impersonate event.
*
* @param Event $event
* @param Auth $auth
* @param User $user The user Impersonating the user in $auth
*/
public function onImpersonate(Event $event, Auth $auth, User $user)
{
$imp = $auth->getUser();
$this->_log($user, sprintf("Impersonated user (%s:%s)", $imp->getId(), $imp->getUsername()));
}
/** /**
* @param Event $event * @param Event $event
* @param User $auth * @param User $auth
@ -78,21 +65,9 @@ class ActivityLog extends Injectable
$this->_log($user, sprintf("OAuth disconnected (%s)", $providerName)); $this->_log($user, sprintf("OAuth disconnected (%s)", $providerName));
} }
/**
* Fired when a activation email is sent.
*
* @param Event $event
* @param User $user
* @param string $providerName
*/
public function onSentActivation(Event $event, User $user)
{
$this->_log($user, sprintf("Activation email sent"));
}
protected function _log(User $user, $message) protected function _log(User $user, $message)
{ {
$ip = (new \Phalcon\Http\Request())->getClientAddress(true); $ip = (new \Phalcon\Http\Request())->getClientAddress();
return (new ActivityLogger())->assign([ return (new ActivityLogger())->assign([
'user_id' => $user->getId(), 'user_id' => $user->getId(),

View file

@ -1,25 +0,0 @@
<?php
namespace App\Listener;
use App\Model\Data\User,
App\Model\Data\UserActivation;
use Phalcon\Di\Injectable,
Phalcon\Events\Event;
class AuthEmailListener extends Injectable
{
public function onSentActivation(Event $event, User $user)
{
$activation = new UserActivation();
$activation->setUserId($user->getId())
->save();
$content = $this->di->getShared('template')->render('mail/account_activation', [
'link' => $activation->getActivationKey()
]);
$this->di->getMail()->send('Httpcb account activation', $user->getEmail(), $content);
}
}

View file

@ -10,12 +10,8 @@ class CreateRequestMetaTable extends AbstractMigration
$table = $this->table('request_meta'); $table = $this->table('request_meta');
$table->addColumn('callbackid', 'integer'); $table->addColumn('callbackid', 'integer');
$table->addForeignKey( $table->addForeignKey('callbackid', 'callback', [ 'id' ],
'callbackid', [ 'constraint' => 'FK_callback' ]);
'callback',
['id'],
['constraint' => 'FK_callback']
);
$table->addColumn('source_ip', 'string', [ $table->addColumn('source_ip', 'string', [
'limit' => 50, 'limit' => 50,

View file

@ -9,12 +9,8 @@ class CreateRequestObjectTable extends AbstractMigration
{ {
$table = $this->table('request_object'); $table = $this->table('request_object');
$table->addForeignKey( $table->addForeignKey('id', 'request_meta', ['id'],
'id', [ 'constraint' => 'FK_request_meta' ]);
'request_meta',
['id'],
['constraint' => 'FK_request_meta']
);
$table->addColumn('headers', 'blob', [ $table->addColumn('headers', 'blob', [
'null' => true, 'null' => true,

View file

@ -265,16 +265,6 @@ class User extends Base
return $this->status == self::STATUS_ACTIVE; return $this->status == self::STATUS_ACTIVE;
} }
/**
* Returns true if this user is suspended.
*
* @return bool
*/
public function isSuspended()
{
return $this->status == self::STATUS_SUSPENDED;
}
/** /**
* @return mixed * @return mixed
*/ */

View file

@ -44,3 +44,4 @@ abstract class Base implements ModuleDefinitionInterface
)); ));
} }
} }

View file

@ -12,8 +12,6 @@
data-bs-toggle="dropdown" role="button" aria-expanded="false"> data-bs-toggle="dropdown" role="button" aria-expanded="false">
{{ icon('solid/user') }} <strong>{{ auth.getUser().username }}</strong> {{ icon('solid/user') }} <strong>{{ auth.getUser().username }}</strong>
{% set imp = auth.getImpersonator() %}
{% if imp %}( {{ icon('solid/user-secret') }} {{ imp.username }} ){% endif %}
</a> </a>
<ul class="dropdown-menu navigation-user-menu-dropdown-list"> <ul class="dropdown-menu navigation-user-menu-dropdown-list">

View file

@ -56,13 +56,6 @@
{% if (user.getId()) %} {% if (user.getId()) %}
{% set actions = [ 'Activate': 'Active', 'Suspend': 'Suspended', 'Delete': 'Deleted' ] %} {% set actions = [ 'Activate': 'Active', 'Suspend': 'Suspended', 'Delete': 'Deleted' ] %}
<div class="float-end"> <div class="float-end">
{% if user.isSuspended() %}
<a class="button button-info" href="{{ url(['for': 'backend-user-activation-email', 'id': user.getId() ]) }}">
Send activation email
</a>
{% endif %}
{% for label, status in actions %} {% for label, status in actions %}
{% if (user.status != status) %} {% if (user.status != status) %}

View file

@ -20,7 +20,6 @@
<th>Email</th> <th>Email</th>
<th>Type</th> <th>Type</th>
<th>Status</th> <th>Status</th>
<th>&nbsp;</th>
</tr> </tr>
</thead> </thead>
@ -37,12 +36,7 @@
<td>{{ item.name }}</td> <td>{{ item.name }}</td>
<td>{{ item.email }}</td> <td>{{ item.email }}</td>
<td>{{ item.type | capitalize }}</td> <td>{{ item.type | capitalize }}</td>
<td><span class="badge {{ item.isActive() ? 'badge-success' : 'badge-danger' }}">{{ item.status }}</span></td> <td>{{ item.status }}</td>
<td>
<a title="Impersonate" href="{{ url(['for': 'backend-user-impersonate', 'id': item.id ]) }}">
{{ icon('solid/user-secret') }}
</a>
</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View file

@ -7,7 +7,6 @@
"ext-psr": ">=1.2", "ext-psr": ">=1.2",
"ext-redis": "*", "ext-redis": "*",
"ext-yaml": "*", "ext-yaml": "*",
"ext-mbstring": "*",
"robmorgan/phinx": "^0.10.6", "robmorgan/phinx": "^0.10.6",
"league/oauth2-client": "^2.3", "league/oauth2-client": "^2.3",
"league/oauth2-github": "^2.0", "league/oauth2-github": "^2.0",

5
package-lock.json generated
View file

@ -1,12 +1,11 @@
{ {
"name": "httpcb", "name": "httpcb",
"version": "1.1.0", "version": "1.1",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "httpcb", "version": "1.1",
"version": "1.1.0",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"@popperjs/core": "^2.11.5", "@popperjs/core": "^2.11.5",

View file

@ -1,6 +1,6 @@
{ {
"name": "httpcb", "name": "httpcb",
"version": "1.1.0", "version": "1.1",
"description": "HTTP Callback Tool", "description": "HTTP Callback Tool",
"devDependencies": { "devDependencies": {
"@popperjs/core": "^2.11.5", "@popperjs/core": "^2.11.5",