Unit tests failing when I use a custom view helper in the layout - phpunit

So I have created my custom view helper and used it in layout.phtml like this:
<?php echo $this->applicationBar(); ?>
It is working flawlessly in the browser but my unit tests that were working before are failing now:
1) UnitTests\Application\Controller\IndexControllerTest::testIndexActionCanBeAccessed
Zend\ServiceManager\Exception\ServiceNotFoundException: Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for applicationBar
When I comment out the view helper in the layout file, test passes again.

I have the same problem, and i solved it in not a good way (but it solved for my specific problem).
The phpunit tests is not finding my factories view helpers, but it is finding my invokables. Then, i did the following:
public function getViewHelperConfig() {
return array(
'factories' => array(
'aplicationBar' => function($service) {
$applicationBar = new ApplicationBar();
return $applicationBar;
},
),
'invokables' => array(
'applicationBar' => 'Application\View\Helper\ApplicationBar',
),
);
When i use the browser, it uses the correct Factory. When i use phpunit, it uses the invokables.
The problem happens when i need to set some parameters. And then, i set some default parameters, that will be used only by phpunit.
namespace Application\View\Helper;
use Zend\View\Helper\AbstractHelper;
class ApplicationBar extends AbstractHelper {
protected $parameter;
public function __construct($parameter = 'something') {
$this->parameter = $parameter;
}
public function __invoke() {
return $this->parameter;
}
}
It is not the best solution, but if i solve it in a better way, i will post here.

Related

Silverstripe 4 save variable in database

Is posible to save SS template variable in database from CMS and after execute it in template?
Okay lets see example:
In CMS i have settings where i put social media links and contact informatios.
Also in CMS i have module where i create HTML block-s which after that i loop in website.
In that html block i want to put existing $SiteConfig.Email variable.
I Try that but that is rendered in template like $SiteConfig.Email not show real email?
Is this posible to do or i need some extra modification?
Check photo
The question you have written makes no sense to me, but I understand the screenshot.
So, SilverStripe renders .ss files with a class called SSViewer. Basically it reads the file as string and then runs it through SSViewer to generate the HTML output.
But, as you saw, the output of variables is not processed.
I can think of 3 ways to get what you want:
Run the variables through SSViewer aswell (in this example, use $RenderedHTMLContent in the template)
class MyDataObject extends DataObject {
private static array $db = [
'Title' => DBVarchar::class,
'HTMLContent' => DBText::class,
];
public function Foobar() { return "hello from foobar"; }
public function RenderedHTMLContent() {
$template = \SilverStripe\View\SSViewer::fromString($this->HTMLContent);
// using $this->renderWith() will allow you access to all things of $this in the template. so eg $ID, $Title or $Foobar. Probably also $SiteConfig because it's global
return $this->renderWith($template);
// if you want to add extra variables that are not part of $this, you can also do:
return $this->renderWith($template, ["ExtraVariable" => "Hello from extra variable"]);
// if you do not want $this, you can do:
return (new ArrayData(["MyVariable" => "my value"]))->renderWith($template);
}
}
Please be aware of the security implications this thing brings. SilverStripe is purposely built to not allow content authors to write template files. A content author can not only call the currently scoped object but also all global template variables. This includes $SiteConfig, $List, .... Therefore a "bad" content author can write a template like <% loop $List('SilverStripe\Security\Member') %>$ID $FirstName $LastName $Email $Salt $Password<% end_loop %> or perhaps might access methods that have file access. So only do this if you trust your content authors
Use shortcodes instead of variables. But I never liked shortcodes, so I don't remember how they work. You'll have to lookup the docs for that.
Build your own mini template system with str_replace.
class MyDataObject extends DataObject {
private static array $db = [
'Title' => DBVarchar::class,
'HTMLContent' => DBText::class,
];
public function Foobar() { return "hello from foobar"; }
public function RenderedHTMLContent() {
return str_replace(
[
'$SiteConfig.Title',
'$SiteConfig.Tagline',
'$Title',
'$Foobar',
],
[
SiteConfig::current_site_config()->Title,
SiteConfig::current_site_config()->Tagline,
$this->Title,
$this->Foobar(),
],
$this->HTMLContent
);
}
}

How to put an Elemental field under a tab in admin CMS form

Starting out with the Elemental module for Silverstripe 4 and by default it lists the Elemental area(s) under the Main "Content" tab. I'd like to put them under their own tab.
How do I do that in my Page class getCMSField function?
What I have is:
A specific page (ElementPage) for using the module
ElementPage:
extensions:
- DNADesign\Elemental\Extensions\ElementalPageExtension
In ElementPage.php I have two $has_one like this:
private static $has_one = [
'LeftElemental' => ElementalArea::class,
'RightElemental' => ElementalArea::class
];
Those work fine, fields display and can render them in the template.
Trying to put them under their own tab, the getCMSFields:
public function getCMSFields()
{
$fields = parent::getCMSFields();
// To remove the default added one
$fields->removeByName('ElementalArea');
$fields->addFieldToTab('Root.LeftContentBlocks', ElementalArea::create('LeftElementalID'));
return $fields;
}
Resulting error:
[User Warning] DataObject::__construct passed The value
'LeftElementalID'. It's supposed to be passed an array, taken straight
from the database. Perhaps you should use DataList::create()->First();
instead?
I didn't really expect that to work but I can't see the create signature it needs.
EDIT:
This seems to get it done:
public function getCMSFields()
{
$fields = parent::getCMSFields();
// To remove the default added one
$fields->removeByName('ElementalArea');
$fields->addFieldToTab('Root.LeftContentBlocks', ElementalAreaField::create('LeftElemental', $this->LeftElemental(), $this->getElementalTypes()));
$fields->addFieldToTab('Root.RightContentBlocks', ElementalAreaField::create('RightElemental', $this->RightElemental(), $this->getElementalTypes()));
return $fields;
}
I'm not entirely sure $this->getElementalTypes() is what I should be doing. Any improvements/corrections are welcomed.

Symfony EventSubscriber: The Page isn't redirecting properly

I have been recently researching EventListeners for the kernal for Symfony4 and I thought I had grasped the basic concept of it but I seem to get a page isn't redirecting properly issue with my EventSubscriber.
Essentially I'd like to do the following logic:
if file_exists $file
redirect to file
else
carry on as normal
Which is how I originally came to kernel.response. Here is my current code:
<?php
namespace App\EventSubscriber;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;
class MaintenanceSubscriber implements EventSubscriberInterface
{
public function onKernelResponse(FilterResponseEvent $event)
{
if (!$event->isMasterRequest()) {
return;
}
if (file_exists('maintenance.flag')) {
$response = new RedirectResponse('maintenance');
$event->setResponse($response);
}
}
public static function getSubscribedEvents()
{
return array(
KernelEvents::RESPONSE => 'onKernelResponse'
);
}
}
this does my logic more or less perfectly, when maintenance.flag doesn't exist it carries on the project as expected, but when I touch maintenance.flag it gets the infamous Firefox page of Page isn't redirecting properly.
I'm not sure if I'm missing something?
I've set up my route:
maintenance:
path: /maintenance
controller: App\Controller\Maintenance\FlagController::flag
which is just a render function - I have a feeling that this could be causing the issue (an endless loop of redirect to flag() which then performs the before action?) but I'm not sure how to render my template from the setResponse() method
Even with the routing conf commented out, I still get the error. So not 100% sure anymore that it's the flag() endless loop theory
I was right indeed about the endless loop being the issue, adding this conditional to exclude the /maintenance url got it to work:
if (strpos($event->getRequest()->getRequestUri(), 'maintenance') !== false) {
return;
}

How can I pass variables to login and register view in laravel 5.3

I am learning and developing a project in laravel 5.3. So I stucked at a point that, to every view in this project I am passing variables to views like following.
public function index()
{
$page_title = 'Page Title';
return view('home', ['title' => $page_title]);
}
so in login and register controllers there are no methods to return views. And I want to pass same variables with different string values to login and registration form. so how can i do that. And secong thing I want to ask is, that how can I add a 404 error page in my project for undefined routes. and third question is that can I set 404 page to register route (www.myproject.com/register) after adding some users in my project. looking forword for reply..
The methods to return the views are in traits, if you want to add you own logic for these methods you can simply override them by adding your own methods the class that uses the trait e.g.
RegisterController
public function showRegistrationForm()
{
$title = 'Register';
return view('auth.register', compact('register'));
}
LoginController
public function showLoginForm()
{
$title = 'Login';
return view('auth.login', compact('title'));
}
If you want to add a custom 404 error page then you just need to create that a file at resources/views/errors/404.blade.php as shown in the docs https://laravel.com/docs/5.4/errors#custom-http-error-pages
Laravel comes with a RedirectIfAuthenticated middleware that will (as the name suggests) rediect the user away from a route if they are already logged in. By default, the login and register routes already have this. If you want to change this behaviour just edit your App\Http\Middleware\RedirectIfAuthenticated class.
Hope this helps!
In the login controller > AuthenticatesUsers trait you can type your variables here.
Default path: app/Http/Controllers/Auth/LoginController.php
public function showLoginForm()
{
$test = 'test';
return view('auth.login', compact('test'));
}
Optimized this could save your 'arss'
LoginController.php
public function showLoginForm()
{
$title = 'Login';
$css = array(
asset('sign-up-in/css/index.css'),
);
$js = array(
asset('sign-up-in/js/index.js'),
);
return view('auth.login', compact('title','css','js'));
}
Do the same for RegisterController.php +(bonus) every other controller
Now slay it all with this
app.blade.php
#if(isset($js))
#foreach($js as $key => $value)
<script src="{{$value}}"></script>
#endforeach
#endif
#if(isset($css))
#foreach($css as $key => $value)
<link href="{{$value}}" rel="stylesheet">
#endforeach
#endif

Can I use a static method as a menu callback in drupal?

When defining a hook_menu item can I use a public static method on a class rather than using the global underscore naming convention drupal tends to go for?
For example, is the following acceptable?
$items['test'] = array(
'page callback' => 'MyClass::test',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK
);
menu_execute_active_handler(), which is the Drupal function that calls the menu callback, contains the following code:
if ($router_item = menu_get_item($path)) {
if ($router_item['access']) {
if ($router_item['file']) {
require_once($router_item['file']);
}
return call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);
}
else {
return MENU_ACCESS_DENIED;
}
}
In PHP 5.2.3, or higher, is possible to call call_user_func() as call_user_func('MyClass::myCallbackMethod').
The only problem I can see is with third-party modules that don't expect a menu callback is a class static method, and use function_exists($menu_callback).
Then, as Coder1 reported, if Drupal core modules, or other modules, try to call the menu_callback using code similar to the following, then they could cause a PHP error.
$menu_callback = $router_item['page_callback'];
$menu_callback($router_item['page_arguments']);
Yes, it should work, as this does:
class Test {
static function method() { echo 'Yes'; }
}
$name = 'Test::method';
call_user_func_array($name, array());
However, why would you want to do that?
As you said, It is common to use normal functions (which you can have lazy loaded when necessary by the menu system automatically) for page callbacks.
If you work with Drupal, you should follow the official coding standard. Even if this is for custom modules only. If someone needs to pick up your work at some point, it will be easier for them if the code follows the same standard that is used everywhere else.
See also http://groups.drupal.org/node/20728#comment-71907

Resources