i overrided the twig error pages and i've got an extension to get some settings for layout but i can't access this extension in the error pages
Here is my extension
/**
* Class ThemeExtension.
*/
class ThemeExtension extends AbstractExtension
{
use SettingManagerTrait;
/**
* #var TokenStorageInterface
*/
private $tokenStorage;
/**
* #var AuthorizationCheckerInterface
*/
private $authorizationChecker;
/**
* #var RequestStack
*/
private $stack;
/**
* #var array
*/
private $available_colors;
/**
* ThemeExtension constructor.
*
* #param TokenStorageInterface $tokenStorage
* #param AuthorizationCheckerInterface $authorizationChecker
* #param RequestStack $stack
* #param array $available_colors
*/
public function __construct(TokenStorageInterface $tokenStorage, AuthorizationCheckerInterface $authorizationChecker, RequestStack $stack, array $available_colors)
{
$this->tokenStorage = $tokenStorage;
$this->authorizationChecker = $authorizationChecker;
$this->stack = $stack;
$this->available_colors = $available_colors;
}
/**
* #return array
*/
public function getFunctions(): array
{
return [
new TwigFunction('theme_name', [$this, 'getThemeName']),
];
}
/**
* #return string
*
* #throws NonUniqueResultException
*/
public function getThemeName(): string
{
// stuff
}
}
I would like to know how to use this in error pages if somebody knows. Thank you for any help or idea. Maybe it's not possible
Related
I have a controller method which I am using to "collect" variables to be assigned to template. I have overridden controller's render() method to merge "collected" and render parameters and assign them to template.
Example:
class Controller extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
private $jsVars = [];
protected function addJsVar($name, $value)
{
$this->jsVars[$name] = $value;
}
public function render($view, array $parameters = [], Response $response = null)
{
return parent::render($view, array_merge($parameters, ['jsVars' => $this->jsVars], $response);
}
public function indexAction()
{
// collect variables for template
$this->addJsVar('foo', 'bar');
return $this->render('#App/index.html.twig', ['foo2' => 'bar2']);
}
}
I just upgraded to Symfony 3.4 which complains that since Symfony4 I am not allowed to override render() method as it will be final.
How could I make it work seamlessly, i.e without defining a new method?
I know about Twig globals but these dont help me
I could use a service to collection variables and inject that service to Twig but that seems odd
Are there events I could listen, e.g TwigPreRender or smth?
You can render a controller from inside Twig like that:
{{ render(controller('App\\Controller\\YourController::yourAction', { 'args': 'hi' })) }}
Documentation here
Seems that there is no easy way.
Basically there are 2 options:
create your own template engine by extending current Symfony\Bundle\TwigBundle\TwigEngine
decorate current templating engine service templating.engine.mytwig
I chose the latter.
Few explanations:
I created service templating.engine.mytwig which decorates current engine templating.engine.twig. Class will get current ´TwigEngine` as input and I'll delegate most of the stuff to it
The class also needs to be twig extension by implementing \Twig_ExtensionInterface (or extending \Twig_Extension was sufficient for me). Also service needs to have tag twig.extension. Otherwise you'll end up having errors such as "Cannot find private service 'assetic' etc"
setParameter/getParameter are for collecting and returning parameters
Then I added shortcut methods to my Controller - setJsVar
Twig template requires also handling of those variables, preferably somewhere in the layout level. But that is not included here
One could you this solution to collect arbitrary template parameters, e.g if you want to assign from another method or whatever
It would be good idea to clear collected parameters after render
Was that all worth it? I dont know :) Cannot understand why Symfony team chose to make Controller::render final in the first place. But anyway here it is:
TwigEnging class:
namespace My\CommonBundle\Component\Templating\MyTwigEngine;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
use Symfony\Bundle\TwigBundle\TwigEngine;
use Symfony\Component\HttpFoundation\Response;
class MyTwigEngine extends \Twig_Extension implements EngineInterface
{
/**
* #var TwigEngine $twig Original Twig Engine object
*/
private $twig;
/**
* #var array $parameters Collected parameters to be passed to template
*/
private $parameters = [];
/**
* MyTwigEngine constructor.
*
* #param TwigEngine $twig
*/
public function __construct(TwigEngine $twig)
{
$this->twig = $twig;
}
/**
* "Collects" parameter to be passed to template.
*
* #param string $key
* #param mixed $value
*
* #return static
*/
public function setParameter($key, $value)
{
$this->parameters[$key] = $value;
return $this;
}
/**
* Returns "collected" parameter
*
* #param string $key
* #return mixed
*/
public function getParameter($key, $default = null)
{
$val = $this->parameters[$key] ?? $default;
return $val;
}
/**
* #param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
* #param array $parameters
*
* #return string
* #throws \Twig\Error\Error
*/
public function render($name, array $parameters = array())
{
return $this->twig->render($name, $this->getTemplateParameters($parameters));
}
/**
* #param string $view
* #param array $parameters
* #param Response|null $response
*
* #return Response
* #throws \Twig\Error\Error
*/
public function renderResponse($view, array $parameters = array(), Response $response = null)
{
return $this->twig->renderResponse($view, $this->getTemplateParameters($parameters), $response);
}
/**
* #param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
*
* #return bool
*/
public function exists($name)
{
return $this->twig->exists($name);
}
/**
* #param string|\Symfony\Component\Templating\TemplateReferenceInterface $name
*
* #return bool
*/
public function supports($name)
{
return $this->twig->supports($name);
}
/**
* #param $name
* #param array $parameters
*
* #throws \Twig\Error\Error
*/
public function stream($name, array $parameters = array())
{
$this->twig->stream($name, $this->getTemplateParameters($parameters));
}
/**
* Returns template parameters, with merged jsVars, if there are any
* #param array $parameters
* #return array
*/
protected function getTemplateParameters(array $parameters = [])
{
$parameters = array_merge($this->parameters, $parameters);
return $parameters;
}
}
Decorator service (services.yml):
services:
templating.engine.mytwig:
decorates: templating.engine.twig
class: My\CommonBundle\Component\Templating\MyTwigEngine
# pass the old service as an argument
arguments: [ '#templating.engine.mytwig.inner' ]
# private, because you probably won't be needing to access "mytwig" directly
public: false
tags:
- { name: twig.extension }
Base controller alteration:
namespace My\CommonBundle\Controller;
use My\CommonBundle\Component\Templating\MyTwigEngine;
abstract class Controller extends \Symfony\Bundle\FrameworkBundle\Controller\Controller
{
/**
* Allows to set javascript variable from action
*
* It also allows to pass arrays and objects - these are later json encoded
*
* #param string $name Variable name
* #param mixed $value - string|int|object|array
*
* #return static
*/
protected function setJsVar($name, $value)
{
/** #var MyTwigEngine $templating */
$templating = $this->getTemplating();
if (!$templating instanceof MyTwigEngine) {
throw new \RuntimeException(sprintf(
'Method %s is implemented only by %s', __METHOD__, MyTwigEngine::class
));
}
$jsvars = $templating->getParameter('jsVars', []);
$jsvars[$name] = $value;
$templating->setParameter('jsVars', $jsvars);
return $this;
}
/**
* Returns templating service
* #return null|object|\Twig\Environment
*/
private function getTemplating()
{
if ($this->container->has('templating')) {
$templating = $this->container->get('templating');
} elseif ($this->container->has('twig')) {
$templating = $this->container->get('twig');
} else {
$templating = null;
}
return $templating;
}
}
I'm new to CXF and JAXB. I'm having problems with a RESTful client that calls a external web service. I think I've followed the steps necessary to do this but I'm getting the following error when executing the client:
SEVERE: No message body reader has been found for class com.jaxb.AcXML, ContentType: text/html
Exception in thread "main" javax.ws.rs.client.ResponseProcessingException: No message body reader has been found for class com.jaxb.AcXML, ContentType: text/html
at org.apache.cxf.jaxrs.impl.ResponseImpl.reportMessageHandlerProblem(ResponseImpl.java:433)
at org.apache.cxf.jaxrs.impl.ResponseImpl.doReadEntity(ResponseImpl.java:384)
at org.apache.cxf.jaxrs.client.AbstractClient.readBody(AbstractClient.java:512)
at org.apache.cxf.jaxrs.client.WebClient.handleResponse(WebClient.java:1173)
at org.apache.cxf.jaxrs.client.WebClient.doResponse(WebClient.java:1156)
at org.apache.cxf.jaxrs.client.WebClient.doChainedInvocation(WebClient.java:1092)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:894)
at org.apache.cxf.jaxrs.client.WebClient.doInvoke(WebClient.java:865)
at org.apache.cxf.jaxrs.client.WebClient.invoke(WebClient.java:428)
at org.apache.cxf.jaxrs.client.WebClient.get(WebClient.java:611)
at com.ws.GetOpenPO.getOpenPOs(GetOpenPO.java:58)
at com.ws.GetOpenPO.main(GetOpenPO.java:79)
My IDE is Eclipse Indigo. I've created a Dynamic Web Project and included the CXF 2.x Web Services, JAX-RS and JAXB facets. Below is the class I'm testing with:
package com.ws;
import java.text.MessageFormat;
import javax.ws.rs.core.MediaType;
import org.apache.cxf.jaxrs.client.WebClient;
import org.apache.cxf.transport.http.HTTPConduit;
import org.apache.cxf.transports.http.configuration.HTTPClientPolicy;
import com.jaxb.AcXML;
public class GetOpenPO {
private String baseUrl;
public AcXML getOpenPOs( )
{
// Create a WebClient pointing to the base URL of the RESTful web service
WebClient client = WebClient.create( baseUrl ).path("GetOpenPOs");
HTTPConduit http = WebClient.getConfig(client).getHttpConduit();
HTTPClientPolicy httpClientPolicy=new HTTPClientPolicy();
httpClientPolicy.setReceiveTimeout(1000000);
httpClientPolicy.setConnectionTimeout(1000000);
httpClientPolicy.setProxyServer("proxy1.global.mycompany.com");
httpClientPolicy.setProxyServerPort(8080);
http.setClient(httpClientPolicy);
// Set the path from which we wish to get the object, request XML, and use JAXB
AcXML POs= client.accept(MediaType.APPLICATION_XML)
.query("U", "parm1")
.query("P", "parm2")
.query("N", "parm3")
.query("Processed", "parm4")
.query("StationEnd", "")
.get( AcXML.class);
return POs;
}
public String getBaseUrl()
{
return baseUrl;
}
public void setBaseUrl( String baseUrl )
{
this.baseUrl = baseUrl;
}
public static void main(String args[]){
GetOpenPO x = new GetOpenPO ();
x.setBaseUrl("http://www24.externalws.net/webservices/webservices.asmx");
AcXML openPOs = x.getOpenPOs();
}
}
I was able to call the webservice via my browser, which returns an XML document. Using this XML I created a schema and then using the "JAXB Classes from Schema" wizard in Eclipse the JAXB objects were generated. Below is the source file for the AcXML entity that I'm trying to retrieve via the client:
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// See http://java.sun.com/xml/jaxb
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2015.08.05 at 10:18:48 AM EDT
//
package com.jaxb;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType>
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element ref="{https://www.externalws.net/acXMLSchema.xsd}Header"/>
* <element ref="{https://www.externalws.net/acXMLSchema.xsd}Request"/>
* </sequence>
* <attribute name="lang" use="required">
* <simpleType>
* <restriction base="{http://www.w3.org/2001/XMLSchema} string">
* </restriction>
* </simpleType>
* </attribute>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "", propOrder = {
"header",
"request"
})
#XmlRootElement(name = "acXML")
public class AcXML {
#XmlElement(name = "Header", required = true)
protected Header header;
#XmlElement(name = "Request", required = true)
protected Request request;
#XmlAttribute(name = "lang", required = true)
protected String lang;
/**
* Gets the value of the header property.
*
* #return
* possible object is
* {#link Header }
*
*/
public Header getHeader() {
return header;
}
/**
* Sets the value of the header property.
*
* #param value
* allowed object is
* {#link Header }
*
*/
public void setHeader(Header value) {
this.header = value;
}
/**
* Gets the value of the request property.
*
* #return
* possible object is
* {#link Request }
*
*/
public Request getRequest() {
return request;
}
/**
* Sets the value of the request property.
*
* #param value
* allowed object is
* {#link Request }
*
*/
public void setRequest(Request value) {
this.request = value;
}
/**
* Gets the value of the lang property.
*
* #return
* possible object is
* {#link String }
*
*/
public String getLang() {
return lang;
}
/**
* Sets the value of the lang property.
*
* #param value
* allowed object is
* {#link String }
*
*/
public void setLang(String value) {
this.lang = value;
}
}
Can someone tell me what I'm doing wrong or what am I missing that's causing the client to fail?
Thank You.
Add JAXBProvider
List<JAXBElementProvider> list = new ArrayList<JAXBElementProvider>();
list.add(new JAXBElementProvider());
WebClient client = WebClient.create( baseUrl, list).path("GetOpenPOs");
How can I set the default value in field.
In my document I need to set default value false for field emailnotify
In mogodb th default value should be zero.
Check my document
namespace xxx\xxxBundle\Document;
use FOS\UserBundle\Document\User as BaseUser;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
/**
* #MongoDB\Document
*/
class User extends BaseUser
{
/**
* #MongoDB\Id(strategy="auto")
*/
protected $id;
/**
* #MongoDB\Boolean
*/
protected $emailnotify;
/**
* Sets the emailnotify.
*
* #param boolean $emailnotify
*
* #return User
*/
public function setEmailnotify($emailnotify)
{
$this->emailnotify = (Boolean) $emailnotify;
return $this;
}
/**
* #return boolean
*/
public function isEmailnotify()
{
return $this->emailnotify;
}
}
I have found that setting the default value in the constructor works
public function __construct() {
$this->emailnotify = false;
}
Of course just setting the class variable to false will work for most parts if you use Doctrine to fetch the Document again afterwards, but the property will not be persisted to MongoDB like with the above.
A lot of times in a Zend Framework 2 view I'll be calling $this->escapeHtml() to make sure my data is safe. Is there a way to switch this behaviour from a blacklist to a whitelist?
PS: Read an article from Padraic Brady that suggests that automatic escaping is a bad idea. Additional thoughts?
You could write your own ViewModel class which escapes data when variables are assigned to it.
Thanks to Robs comment, I extended the ZF2 ViewModel as follows:
namespace Application\View\Model;
use Zend\View\Model\ViewModel;
use Zend\View\Helper\EscapeHtml;
class EscapeViewModel extends ViewModel
{
/**
* #var Zend\View\Helper\EscapeHtml
*/
protected $escaper = null;
/**
* Proxy to set auto-escape option
*
* #param bool $autoEscape
* #return ViewModel
*/
public function autoEscape($autoEscape = true)
{
$this->options['auto_escape'] = (bool) $autoEscape;
return $this;
}
/**
* Property overloading: get variable value;
* auto-escape if auto-escape option is set
*
* #param string $name
* #return mixed
*/
public function __get($name)
{
if (!$this->__isset($name)) {
return;
}
$variables = $this->getVariables();
if($this->getOption('auto_escape'))
return $this->getEscaper()->escape($variables[$name]);
return $variables[$name];
}
/**
* Get instance of Escaper
*
* #return Zend\View\Helper\EscapeHtml
*/
public function getEscaper()
{
if (null === $this->escaper) {
$this->escaper = new EscapeHtml;
}
return $this->escaper;
}
}
In a Controller it could be used like this:
public function fooAction()
{
return new EscapeViewModel(array(
'foo' => '<i>bar</i>'
));
//Turn off auto-escaping:
return new EscapeViewModel(array(
'foo' => '<i>bar</i>'
),['auto_escape' => false]);
}
Question:
I would appreciate it if soemebody would comment, if this is best practice or if there is a better and ecp. more efficient and resource-saving way?
The #param tag allow the documentation of properties, e.g.
/**
* #param {Object} userInfo Information about the user.
* #param {String} userInfo.name The name of the user.
* #param {String} userInfo.email The email of the user.
*/
How would I document the properties of the #this tag?
/**
* #this {Object}
* #param {String} this.name The name of the user.
* #param {String} this.email The email of the user.
*/
I'm wondering if anyone working on the project knows. (The docs are still being created...)
To document instance members, use #name Class#member:
/**
* Construct a new component
*
* #class Component
* #classdesc A generic component
*
* #param {Object} options - Options to initialize the component with
* #param {String} options.name - This component's name, sets {#link Component#name}
* #param {Boolean} options.visible - Whether this component is visible, sets {#link Component#visible}
*/
function Component(options) {
/**
* Whether this component is visible or not
*
* #name Component#visible
* #type Boolean
* #default false
*/
this.visible = options.visible;
/**
* This component's name
*
* #name Component#name
* #type String
* #default "Component"
* #readonly
*/
Object.defineProperty(this, 'name', {
value: options.name || 'Component',
writable: false
});
}
This results in a Members section in the documentation that lists each member, its type, default value, and whether it's read only.
The output as generated by jsdoc#3.3.0-alpha3 looks like this:
See also:
JSDoc namepaths
#instance tag
#readonly tag
#type tag
Use the #property tag to describe the attribute of an object.
#param is used to define the parameters of a method or constructor.
#this is used to define which object this refers to.
So here's an example using JSDOC 3.
/**
* #class Person
* #classdesc A person object that only takes in names.
* #property {String} this.name - The name of the Person.
* #param {String} name - The name that will be supplied to this.name.
* #this Person
*/
var Person = function( name ){
this.name = name;
};
JSDOC 3: https://github.com/jsdoc3/jsdoc
More information: http://usejsdoc.org/index.html
More info: http://code.google.com/p/jsdoc-toolkit/wiki/TagParam
Within the constructor of a class, jsdoc will realize by itself that the documented properties belong to class intances. So this should be enough:
/**
* #classdesc My little class.
*
* #class
* #memberof module:MyModule
* #param {*} myParam Constructor parameter.
*/
function MyLittleClass(myParam) {
/**
* Instance property.
* #type {string}
*/
this.myProp = 'foo';
}
If it is not clear for jsdoc that the function is the class constructor, you can use #this to define what this refers to:
/**
* #classdesc My little class.
*
* #class
* #memberof module:MyModule
* #name MyLittleClass
* #param {*} myParam Constructor parameter.
*/
// Somewhere else, the constructor is defined:
/**
* #this module:MyModule.MyLittleClass
*/
function(myParam) {
/**
* Instance property.
* #type {string}
*/
this.myProp = 'foo';
}