I'm writing tests for a controller in a plugin (Assets plugin).
This is the controller:
namespace Assets\Controller;
use Cake\Controller\Controller;
class AssetsController extends Controller
{
public function asset($filename, $type)
{
$this->response->type($type);
$this->response->file(ASSETS . DS . $filename);
return $this->response;
}
}
As you can see, it only sends an asset files.
This is the route:
Router::plugin('Assets', ['path' => '/assets'], function ($routes) {
$routes->connect(
'/:type/:filename',
['controller' => 'Assets', 'action' => 'asset'],
[
'type' => '(css|js)',
'filename' => '[a-z0-9]+\.(css|js)',
'pass' => ['filename', 'type'],
]
);
});
And this is the test class:
namespace Assets\Test\TestCase\Controller;
use Assets\Utility\AssetsCreator;
use Cake\TestSuite\IntegrationTestCase;
class AssetsControllerTest extends IntegrationTestCase
{
public function testAsset()
{
//This is the filename
$filename = sprintf('%s.%s', AssetsCreator::css('test'), 'css');
$this->get(sprintf('/assets/css/%s', $filename));
$this->assertResponseOk();
}
}
Running the test, however, this exception is generated (full test here):
1) Assets\Test\TestCase\Controller\AssetsControllerTest::testAsset
Cake\Routing\Exception\MissingControllerException: Controller class could not be found. in /home/mirko/Libs/Plugins/Assets/vendor/cakephp/cakephp/src/Http/ControllerFactory.php:91
I do not think is a problem of broken, because the same exception is generated by doing so:
$url = \Cake\Routing\Router::url([
'controller' => 'Assets',
'action' => 'asset',
'plugin' => 'Assets',
'type' => 'css',
'filename' => $filename,
]);
$this->get($url);
Where am I doing wrong? Thanks.
Solved! On my tests' bootstrap, I missed:
DispatcherFactory::add('Routing');
DispatcherFactory::add('ControllerFactory');
Now it works.
Related
I am creating a REST Api using cakephp-jwt-auth But the AppController I create in subfolder is not called.
My App controller code inside Ca/Api code
<?php
namespace App\Controller\Ca\Api;
use Cake\Controller\Controller;
use Cake\Event\Event;
class AppController extends Controller
{
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Auth', [
'storage' => 'Memory',
'authenticate' => [
'Form' => [
'fields' => ['username' => 'email'],
],
'ADmad/JwtAuth.Jwt' => [
'parameter' => 'token',
'userModel' => 'Users',
'fields' => [
'username' => 'id'
],
'queryDatasource' => true
]
],
'unauthorizedRedirect' => false,
'checkAuthIn' => 'Controller.initialize'
]);
$this->loadComponent('BryanCrowe/ApiPagination.ApiPagination', [
'key' => 'paging',
'aliases' => [
'page' => 'currentPage',
'current' => 'resultCount'
],
'visible' => [
'currentPage',
'resultCount',
'prevPage',
'nextPage',
'pageCount',
'page',
]
]);
}
public function beforeFilter(Event $event) {
parent::beforeFilter($event);
}
}
And my route file:
Router::prefix('ca/api', function ($routes) {
$routes->setExtensions(['json']);
$routes->connect('/login', ['controller' => 'Login', 'action' => 'login', "prefix" => "ca/api"]);
$routes->connect('/dashboard', ['controller' => 'Dashboard', 'action' => 'home', 'prefix' => "ca/api"]);
$routes->fallbacks('InflectedRoute');
});
My app controller class is not called and I don't understand where I am doing wrong.
you can create controller like this
<?php
namespace App\Controller\Api;
use Cake\Controller\Controller;
use Cake\Event\Event;
class AppController extends Controller
{
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Auth', [
'storage' => 'Memory',
'authenticate' => [
'Form' => [
'scope' => ['Users.group_id' => 1]
],
'ADmad/JwtAuth.Jwt' => [
'parameter' => 'token',
'userModel' => 'Users',
'fields' => [
'username' => 'id'
],
'queryDatasource' => true
]
],
'unauthorizedRedirect' => false,
'checkAuthIn' => 'Controller.initialize'
]);
}
}
And other controller like this
<?php
namespace App\Controller\Ca;
use Cake\Event\Event;
use Cake\Http\Exception\UnauthorizedException;
use Cake\Utility\Security;
use Firebase\JWT\JWT;
use Cake\Http\ServerRequest;
use Cake\I18n\Time;
use Aws\S3\S3Client;
use League\Flysystem\AwsS3v3\AwsS3Adapter;
use League\Flysystem\Filesystem;
use Cake\Http\Exception\NotFoundException;
class DashboardController extends AppController
{
public function initialize()
{
parent::initialize();
}
public function home()
{
pr("hiiih");
}
}
Remember do not use
**
use App\Controller\AppController;
**
When you are creating AppController in sub folder
for more information read this tutorial :- https://trinitytuts.com/secure-cakephp-web-services-using-jwt/
The AppController isn't called magically someplace internally in CakePHP. The Routes you define call a specific controller, which should simply extend your AppController.
Following convention you'd typically only ever use a single AppController for your entire application, in /src/Controller/AppController. It looks based on the authentication methods listed in your example, this is the approach your taking, but you don't need to move it into a subfolder to make prefix routing work.
Based on your routes:
Router::prefix('ca/api', function ($routes) {
... This will look for a classes that match connections inside /src/Controller/Ca/Api, and for matches like:
// Note, the "prefix" item you listed on this line is not required I'd remove it:
$routes->connect('/login', ['controller' => 'Login', 'action' => 'login', "prefix" => "ca/api"]);
.. This will look for a class called LoginController, at /src/Controller/Ca/Api/. This class should simply reference your existing default AppController in it's default location:
<?php
namespace App\Controller\Ca\Api;
use App\Controller\AppController; // The namespace declaration is how your subclass locates it's parent class
class LoginController extends AppController
{
If you have some particular need to have multiple AppControllers (which I'd not recommend) then just change what version you're referencing with use namespace.
See for more information:
PHP Namespaces
Prefix Routing
The AppController
I canĀ“t get CakePdf to work, If I would get an error, I could work with it. But nothing really shows up.
CakePhp: 3.1.6
CakePdf: current Version
I added an InvoicesController with this code
<?php
namespace App\Controller;
class InvoicesController extends AppController
{
// In your Invoices controller you could set additional configs, or override the global ones:
public function view($id)
{
$this->pdfConfig = array(
'orientation' => 'landscape',
'download' => true,
'filename' => 'invoucne.pdf'
);
}
}
?>
Also added this to my bootstrap.php
use Cake\Event\EventManager;
EventManager::instance()
->on(
'Controller.initialize',
function (Cake\Event\Event $event) {
$controller = $event->subject();
if ($controller->components()->has('RequestHandler')) {
$controller->RequestHandler->config('viewClassMap.pdf', 'CakePdf.Pdf');
}
}
);
Plugin::load('CakePdf', ['bootstrap' => true, 'routes' => true]);
Configure::write('CakePdf', [
'engine' => 'CakePdf.dompdf',
'margin' => [
'bottom' => 15,
'left' => 50,
'right' => 30,
'top' => 45
],
'orientation' => 'landscape',
'download' => true
]);
This to my routes.php
Router::extensions(['pdf']);
Also I go my default.ctp in the src/Template/Layout/pdf
<h2>Rendered with default layout</h2>
<?php echo $this->fetch('content'); ?>
And my views in my Template/Invoices/pdf
<html>
<body>
<h1>test</h1>
</body>
</html>
My url looks like:
http://localhost/caketest/invoices/view/1.pdf
I installed it with composer and my plugins lie in vendor/dompdf and vendor/friendsofcake/cakepdf
I had this problem before the solution is to put
Router::extensions(['pdf']);
before
Router::scope('/', function ($routes) { //some code}
not after it
Following the CakePHP which looks a bit confusing and not so straight forward, I have created a basic authentication logic, however, I cannot seem to load Auth component.
Here is the code part from the AppController.php:
public function initialize()
{
parent::initialize();
$this->loadComponent('RequestHandler');
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authenticate' => ['Form' => ['fields' => ['username' => 'email', 'password' => 'password']]],
'loginAction' => ['controller' => 'Users', 'action' => 'login'],
'loginRedirect' => ['controller' => 'Groups', 'action' => 'index'],
'logoutRedirect' => ['controller' => 'Users', 'action' => 'login']
]);
}
//Allow basic views
public function beforeFilter(Event $event)
{
$this->Auth->allow(['index', 'view', 'display']);
}
Now no matter which controller or action I run, I always receive the following error:
Error: Call to a member function allow() on a non-object
that is referencing the following line:
$this->Auth->allow(['index', 'view', 'display']);
It has to be a straight forward thing, but I just cannot find it in the docummentation, therefore any help or guidance is much appreciated.
Check that your child controller's method initialize() is calling the parent method.
class MyController extends AppController
{
public function initialize() {
parent::initialize();
//rest of code
}
}
I've got this one when I had no Template/Users/login.ctp template created yet
managed to find out only after inspecting the stack-trace obtained by
$e = new \Exception('How did I got here anyway?');
debug($e->getTraceAsString());
yielding
#5 vendor/cakephp/cakephp/src/Error/ExceptionRenderer.php(318): Cake\Controller\Controller->render('missingTemplate')
I have a little problem with the isAuthorized function.
When I use the public function add, isAuthorized doesn't recognize the action request. I changed the name to 'addv' and now it's working. So why? Why can't I use the word 'add'? I used it twice on another project and I used it once on my current project.
Thanks you for you help !
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Event\Event;
use Cake\Validation\Validator;
class VideosController extends AppController{
public function isAuthorized($user){
if(in_array($this->request->action, ['addv'])){
die();
if($user){
return true;
}
}
return parent::isAuthorized($user);
}
public function addv($idc= null,$idg = null){
debug($idc);
debug($this->request->action);
}
}
?>
AppController
class AppController extends Controller {
public function initialize() {
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'authorize' => ['Controller'],
'loginRedirect' => [
'controller' => 'Pages',
'action' => 'display',
'home'
],
'logoutRedirect' => [
'controller' => 'Pages',
'action' => 'display',
'home'
]
]);
$this->loadComponent('RequestHandler');
$this->set('info_session', $this->Auth->user());
}
public function beforeFilter(Event $event){
$this->Auth->allow(['register']);
$this->Auth->allow(['display']);
$this->Auth->allow(['controller' => 'Users', 'action' => 'add']);
}
public function isAuthorized($user){
if(isset($user['grade']) && $user['grade']=== 3){
return true;
}
}
}
I have this in orders_controller.php
function beforeFilter() {
$this->Auth->allow('checkout', 'checkout_confirm', 'checkout_done');
parent::beforeFilter();
}
When I try to go to orders/checkout it always redirects me to users/login
Don't know where to look for solution.
I have an app_controller.php in app/
class AppController extends Controller {
var $components = array(
'Email',
'RequestHandler',
'Session',
'Cookie',
'Auth' => array(
'fields' => array(
'username' => "email",
'password' => "password"
),
'autoRedirect' => true,
'loginAction' => array('controller' => "users", 'action' => "login", 'admin' => false), // 'loginRedirect' => array('controller'
=> "users", 'action' => "check_account") // 'loginRedirect' => array('admin' => false, 'controller' => "users", 'action' => "account_home")
),
'Acl',
'Loviu'
);
var $helpers = array('Html', 'Form', 'Paginator', 'Session', 'Image', 'Javascript', 'Time', 'Text', 'Embed', 'Loviu');
var $uses = array('User', 'Shelf');
function beforeFilter() {
if (isset($this->params['admin']) && (1 == $this->params['admin'])) {
$this->testAccess("admin");
}
if($this->params['controller'] == 'pages'){
$this->Session->write('menu.active', 'inactive');
}
$this->Auth->allow('display');
if (false == $this->Session->check('Auth.User')) {
if (empty($this->data)) {
$cookie = $this->Cookie->read('Auth.User');
if (false == is_null($cookie)) {
// login user
if ($this->Auth->login($cookie)) {
// delete auth message
$this->Session->delete('Message.auth');
}
else {
// delete invalid cookie
$this->Cookie->delete('Auth.User');
}
} elseif(!$this->Session->read('loggedOut') && $this->params['action'] != 'login_fb') {
$this->__checkFBStatus();
}
}
}
$this->set('user_id', $this->User->id);
$this->set('lng', $this->Cookie->read("language") ? $this->Cookie->read("language") : 'eng');
parent::beforeFilter();
}
I would also put the $this->Auth->allow('checkout', 'checkout_confirm', 'checkout_done');line in your app_controller. In my experience, sometimes the problem is that the system gets confused about which controller this action belongs to, depending on how your code is setup.
Here is what I use in my app_controller that has been perfect, in case it helps:
function beforeFilter() {
$this->allowAccess();
}
private function allowAccess() {
// this actually searches the URL to see what controller you're accessing, and allows actions for that controller.
if(in_array($this->name, array('Pages'))) {
$this->Auth->allow(array('home','blog','index'));
}
}
This specificity has saved me so much trouble, and calling the Auth->Allow in app_controller is where it really should be. Hope this helps!
I had the same problem and solved for my project.
My cakephp version 3. While you loadcomponent just put loginaction.
class AppController extends BaseController
{
public function initialize()
{
$this->loadComponent('Flash');
$this->loadComponent('Auth', [
'loginAction' => [
'controller' => 'Admin',
'action' => 'login',
'plugin' => 'Admin'
],
'loginRedirect' => [
'controller' => 'admin',
'action' => 'dashboard'
],
'logoutRedirect' => [
'controller' => 'admin',
'action' => 'login'
]
]);
}
}
hope helps others.
#rncrtr's answer worked for me, but I had to add the parent::beforeFilter() to the allowAccess method:
public function beforeFilter() {
parent::beforeFilter();
$this->allowAccess();
}
private function allowAccess() {
if (in_array($this->name, array('Pages'))) {
$this->Auth->allow(array('home','index','display'));
}
}
Oh yeah, I also had to add display to the allow array.
if you work on cakephp 2.x you must do like this :
function beforeFilter(){
$this->Auth->allow(array('action you want to allow1','action you want to allow2'));
}
allow(array()) instead allow()
---put that code into controller have action you want allow access without login
if you use $this->Auth->allow() you must call parent::beforeFilter(); in function beforeFilter() like this :
function beforeFilter(){
parent::beforeFilter();
$this->Auth->allow('add','view');
}