I need to check if a user is using a mobile device upon connecting to the website.
I need to make an eventListener further on.
in the Symfony doc there is a passage where actually you can check this $request->headers->get('User-Agent')
Is there a simple way to do that?
--- EDIT ---
this is the code I wrote so far. I'm missing maybe on how to pass it to the controller?
service
template.loader:
class: ST\BackofficeBundle\EventListener\DeviceListener
tags:
- { name: kernel.event_listener, event: kernel.view, method: onKernelView }
listener
class DeviceListener
{
public function onKernelView(getResponseEvent $event)
{
$event->getRequest()->getSession()->set('mobile', true);
$response = new Response();
$response->setContent($event);
$event->setResponse($response);
}
}
Am I on the right track ?
You could have a look at https://github.com/kbond/ZenstruckMobileBundle code
It creates an EventListener here:
https://github.com/kbond/ZenstruckMobileBundle/blob/master/EventListener/RequestListener.php
And based on that, overwrites the twig render here:
https://github.com/kbond/ZenstruckMobileBundle/blob/master/Twig/TwigEngine.php
Both things get wired up through
https://github.com/kbond/ZenstruckMobileBundle/blob/master/Manager/MobileManager.php
Related
I'm writing a Symfony application. Means: The application has a structure of a Symfony application and uses Symfony's MVC (symfony/http-kernel, symfony/routing etc.), but it should be as framework-independent as possible. So instead of the framework's DI, I'm using PHP-DI.
Now I've added the symfony/event-dispatcher component and to register a listener for all custom events.
services.yaml
system.event_handler:
tags:
- { name: system.event_handler, event: general.user_message_received, method: handle }
- { name: system.event_handler, event: xxx.foo, method: handle }
- { name: system.event_handler, event: yyy.foo, method: handle }
- { name: system.event_handler, event: zzz.foo, method: handle }
The system.event_handler is defined in my PHP-DI dependencies file:
...
EventHandlerInterface::class => DI\factory(function(ContainerInterface $container) {
return new SystemEventHandler(
$container->get('process_handler.xxx'),
$container->get('process_handler.yyy'),
$container->get('process_handler.zzz')
);
}),
'system.event_handler' => DI\get(EventHandlerInterface::class),
...
Now I'm getting an error:
RuntimeException
The definition for "system.event_handler" has no class. If you intend
to inject this service dynamically at runtime, please mark it as
synthetic=true. If this is an abstract definition solely used by child
definitions, please add abstract=true, otherwise specify a class to
get rid of this error.
Why? Is it wrong to use an alias at this paces?
OK, I've replaced the alias in the services.yaml by the FQCN of my event handler:
App\Process\SystemEventHandler:
tags:
- { name: system.event_handler, event: general.user_message_received, method: handle }
- { name: system.event_handler, event: xxx.foo, method: handle }
- { name: system.event_handler, event: yyy.foo, method: handle }
- { name: system.event_handler, event: zzz.foo, method: handle }
No errors anymore. But the event handler is still not get added to the EventDispatcher.
How to register event listeners (defined in the PHP-DI container) in Symfony 4?
PHP-DI is called as a fallback of Symfony's container. So when an entry is not defined in Symfony, it is pulled out of PHP-DI. However I've never tried defining an entry in both containers, from my POV (as the maintainer of PHP-DI) this is not supposed to work.
Since you need to use Symfony tags, the simplest solution is to define that specific service entirely in Symfony's config (not PHP-DI).
Another lead could be to try to write a Symfony compiler pass to try and make this work, but I don't know if that is feasible. If you manage to find a good solution please report it in the GitHub repository, we could try to integrate it.
Default forgot password form is an Email form field. I have a licence number attached to each user. As a user, I want to get the reset password link by entering my licence number instead of email.
I've found a way around to override the functionality side but not the form. What I've done till now
Filename: _config.php
Object::useCustomClass('MemberLoginForm', 'ExtendLoginForm');
Filename: ExtendLoginForm.php
Just as a test - override forgotPassword method
class ExtendLoginForm extends MemberLoginForm
{
public function forgotPassword($data) {
// Ensure password is given
$this->sessionMessage('This works', 'bad');
$this->controller->redirect('Security/lostpassword');
return;
}
}
Session message is successfully printed with a redirect to the same page.
How can I override the form to make it a Text field instead of Email field.
First, just a general tip, you should avoid using Object::useCustomClass() in favour of Injector (https://docs.silverstripe.org/en/3.0/reference/injector/). Object::useCustomClass() is more of a 2.4 thing.
The forgot password form is generated in Security. You can try using a custom implementation of that class as well, and overloading the ForgotPasswordForm method.
config.yml
Injector:
Security:
class: MyCustomSecurity
MyCustomSecurity.php
class MyCustomSecurity extends Security
{
public function LostPasswordForm()
{
$form = parent::LostPasswordForm();
$form->Fields()->replaceField('Email', TextField::create('Email'));
return $form;
}
}
Q1.I want to count the unread messages before every page rendered ,and add the number into twig template.i don't know how to make it
Q2.i have read the symfony tutorial,it seems that i will make a service ,but i don't know how to do it.is the following code right? and i don't know what to write at the seconde argument
namespace App\RepairBundle\Service;
use Symfony\Component\DependencyInjection\ContainerInterface;
use App\RepairBundle\Entity\RepairMessageRepository;
class CountUnReadMessage
{
protected $container;
protected $messageRepository;
public function __construct(ContainerInterface $container,RepairMessageRepository $messageRepository)
{
$this->container = $container;
$this->messageRepository = $messageRepository;
}
public function countUnRead()
{
$number = $this->messageRepository->countByUnread($this->container->get('security.token_storage')->getToken()->getUser()->getId());
return $number;
}
}
parameters:
app.repair.count_unread_message: App\RepairBundle\Service\CountUnReadMessage
services:
app.repair.count_unread_message:
class: %app.repair.count_unread_message%
arguments:
- #service_container
- #
If piece of the twig template containing message counter similar in all templates, you can move it to separate template and call service inside this template. You steps to achieve this might look like this:
Write service for getting message counter (you almost got it, but try to avoid injecting whole container in servce since it is a very bad practice. In this case, i think you could inject only security.token_storage)
Make this service visible in twig templates by declare it in config file.
config.yml
twig:
globals:
count_read_message: #app.repair.count_unread_message
In your separate twig file call this service
message_block.html.twig
{{ count_read_message.countUnRead() }}
And include this twig file to needed template (better idea would be keep main template for most of templates and include you file in this template, but this dependenced of template structure)
I hope you got the main idea =)
P.S. Answering for Q2 - it is #doctrine.orm.entity_manager
If you want to inject repository make another service with your repository:
app.message_repository:
class: Doctrine\ORM\EntityRepository
factory: ["#doctrine.orm.default_entity_manager", getRepository]
arguments:
- App\RepairBundle\Entity\RepairMessage
Then in your service:
app.repair.count_unread_message:
class: %app.repair.count_unread_message%
arguments:
- #service_container
- #app.message_repository
BTW you don't need container, inject only security.token_storage instead of container.
I want to redraw a line in a line chart without reloading it (neither template nor controller) completely when navigating from country/5 to country/7. Can this be done with ui-router?
State
country/:id
Template with directive - country.html
<lineChart data="scope.someData">
Controller
onStateParamsChange => fetch data, set scope.someData
As of today, there is no official support for what you're looking for, which in UI Router parlance is considered 'dynamic parameters'. However, if you check out this experimental branch and help us out by testing and providing feedback, it will get merged to master sooner.
Set up your route/state like so:
$stateProvider.state("country", {
url: "/country/{id:int}",
params: { id: { dynamic: true } }
/* other state configuration here */
});
Then, in your controller, you can observe changes to id like so:
$stateParams.$observe("id", function(val) {
// val is the updated value of $stateParams.id
// Here's where you can do your logic to fetch new data & update $scope
});
I'm using SonataAdminBundle with Symfony 2.2 and want to display the dashboard blocks depending on what user is logged in.
e.g.
Users with Group Superadmin will see blocks 'UserManagement' and 'Messages'
Users with Group Staff will see only block 'Messages'
I read the whole documentation especially the security doc but found no info on how to restrict the dashboard view. I already implemented a filter which will will show no entries in the list view of an entity class if the user's permissions are not enough.
But it would be way better to not show him the blocks at all.
Any ideas on how to do this ?
Well, for anyone running into this, I solved it by simply returning an empty Response in execute(). My use-case was one where I wanted to show a block to users with a specific role:
First I defined my service for using the security.context service like so:
sonata.block.service.foo:
class: Acme\DemoBundle\Block\FooBlockService
arguments: [ "sonata.block.service.foo", "#templating", "#doctrine", "#security.context" ]
tags:
- { name: sonata.block }
Then I defined the block service __construct() as:
//Acme\DemoBundle\Block\FooBlockService
public function __construct($name, EngineInterface $templating, $doctrine, $securityContext)
{
parent::__construct($name, $templating);
$this->doctrine = $doctrine;
$this->securityContext = $securityContext;
}
Finally, in the execute function the first thing I did was check for a specific role on the user like so:
//Acme\DemoBundle\Block\FooBlockService
public function execute(BlockContextInterface $blockContext, Response $response = null)
{
if( $this->securityContext->isGranted("ROLE_ACME_FOO") === false )
{
return new Response();
}
//... more code
This works, but it feels like a hack. Mainly, because the Block goes through all of its stages, and only on the output it return nothing, meaning overhead. A better solution would be to somehow prevent the entire Block from being loaded, based on some custom code.
In conclusion, this isn't the best way, but it worked for me!
You can restrict access to block using roles, like this:
sonata_admin:
dashboard:
blocks:
- { position: top, type: your_service_block_name, class: 'col-xs-4', roles: [ROLE_SOME_NAME_HERE_, ROLE_SOME_NAME_HERE_2] }
You can check under SonataAdminBundle:Core:dashboard.html.twig how this roles works:
{% if block.roles|length == 0 or is_granted(block.roles) %}
<div class="{{ block.class }}">
{{ sonata_block_render({ 'type': block.type, 'settings': block.settings}) }}
</div>{% endif %}