output printer ESC codes from twig in Symfony - php

I have a system that uses Symfony, and is connected to a (citizen ct-s2000) POS printer.
What I currently do is render the string to send to the printer, using the twig service:
$this->templatingService->render('SamPosBundle:Ticket:template1.html.twig', array('order' => $order))
and send this to the printer using fwrite, after that I feed the paper 1 line and cut the paper using:
fwrite($handle, chr(hexdec('0A')));
fwrite($handle, chr(hexdec('1B')).chr(hexdec('69')));
This all works like a charm, however.
Now I am looking for a way to send the escape codes from WITHIN the twig template
so that I can use the codes to underline etc., and finally cut the paper, from inside the twig template.
I'm sure this would involve a twig extension to create an additional filter, which I know how to make, I just don't know WHAT EXACTLY it should do or how to go about the conversion from twig to escape code which would be picked up by fwrite
I've been looking for 2 days, and I really can't seem to figure this out on my own, so any help would be greatly appreciated.

Configure your twig extension:
services:
your.twig.pos_printer_extension:
class: Your\CustomBundle\Twig\POSPrinterExtension
tags:
- { name: twig.extension }
Create your extension:
<?php
namespace Your\CustomBundle\Twig;
class POSPrinterExtension extends \Twig_Extension
{
public function getGlobals()
{
return [
'some_constant' => chr(hexdec('0A'))
];
}
public function getFilters()
{
return [
'bold' => new \Twig_Filter_Method($this, 'bold')
];
}
public function bold($text)
{
return chr(hexdec('0B')) . $text . chr(hexdec('0A'));
}
}
And finally use it in your twig templates:
{{ some_constant }}
{{ receipt.amount | bold }}
You can apply filters to an entire block too:
{% filter bold %}
Dear {{ name }},
{% endfilter %}
Obviously I don't know the correct escaped chars but you got the idea right?
UPDATE (I'm writing this to avoid downvotes)
My example is using Twig_Filter_Method which is deprecated since 1.12 (to be removed in 2.0), you should be using Twig_SimpleFilter instead.

Its alive, just the syntax for the getFilters() was slightly different:
public function getGlobals()
{
return [
'ticket_cut' => chr(hexdec('1B')).chr(hexdec('69'))
];
}
public function getFilters()
{
return array(
new \Twig_SimpleFilter('ticketBold', array($this, 'ticketBold')),
);
}
public function ticketBold($string)
{
return chr(hexdec('1B')).chr(hexdec('45'))."1".$string.chr(hexdec('1B')).chr(hexdec('45'))."0";
}
Thx for pointing me in the right direction!

Related

Using a PHP function in Twig

I have a PHP code to add a new class for my Twig template in my common controller in: "opencart\htdocs\catalog\controller\common\cart.php"
The code should check if the Device is Mobile or not.
function onStart()
{
// Anonymous Class only working on PHP7
$this['code'] = new class {
public function MobileDetect() {
return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo
|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i"
, $_SERVER["HTTP_USER_AGENT"]);
}
};
}
But now I don´t know how to address that function correctly from my twig side at:
opencart\htdocs\catalog\view\theme\default\template\common\cart.twig
I tried something like this, but it didn´t seem to work:
{% if code.MobileDetect() is defined %}
If a device is mobile I want to use a completely different HTML construct.
What you need to do is create your own Twig function:
https://twig.symfony.com/doc/3.x/advanced.html#functions
So using their examples you should be able to do something like
$function = new \Twig\TwigFunction('MobileDetect', function () {
return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo
|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i"
, $_SERVER["HTTP_USER_AGENT"]);
});
$twig->addFunction($filter);
Then call it like
{% if MobileDetect() %}

Including SVG in Twig template

I am changing my assets from PNGs to SVGs, where appropriate, however I am having difficultly including these SVGs in my Twig templates.
I am trying to include the SVG like this:
{{ source('/assets/img/crmpicco-horizontal-logo.svg') }}
However, this results in the following error:
Unable to find template "/assets/img/crmpicco-horizontal-logo.svg"
(looked into:
/Library/WebServer/Documents/crmpicco/symfony/app/Resources/views,
/Library/WebServer/Documents/crmpicco/symfony/vendor/symfony/symfony/src/Symfony/Bridge/Twig/Resources/views/Form).
Why can't I include this SVG in the same directory as my other assets? Specifically, i'm interested in finding out why a SVG can't be treated as an asset with Assetic.
Here is the solution I use (Twig with Symfony 4 and Webpack Encore) :
Inside your twig config, declare your public path :
twig:
paths:
"%kernel.project_dir%/public": public_path
Load your svg asset like that :
{{ source('#public_path'~asset('build/images/your_file.svg')) }}
You have to rename your svg file with a twig extension crmpicco-horizontal-logo.svg.twig and do this :
{% include ":svg:crmpicco-horizontal-logo.svg.twig" %}
The folder svg is in app/Ressources/views in this example
The #adrien-lamotte solution was working for inlining svg tag. However I also needed to dynamically add class(es) on the tag.
I ended up coding a specialized Twig extension for loading SVG and adding attributes, so you do:
{{ source(svg_asset('/images/front/connexion.svg'))|add_class('svg-picto') }}
The svg_asset function is just to limit boilerplate; whereas the add_class and add_html_attr filters allows for the SVG DOM manipulation.
The extension class:
<?php
namespace App\Twig\Extension;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
use Twig\TwigFilter;
class SvgExtension extends AbstractExtension
{
private $assetExt;
public function __construct(
\Symfony\Bridge\Twig\Extension\AssetExtension $assetExt
) {
$this->assetExt = $assetExt;
}
public function getFunctions(): array
{
// We could use a macro also, but this would need to be imported
// each time.
// https://twig.symfony.com/doc/3.x/tags/macro.html
return array(
new TwigFunction('svg_asset', [$this, 'svg_asset_url']),
);
}
public function getFilters(): array
{
return array(
new TwigFilter('add_html_attr', array($this, 'add_html_attr'), [
'is_safe' => ['html'],
]),
new TwigFilter('add_class', array($this, 'add_class'), [
'is_safe' => ['html'],
]),
);
}
public function svg_asset_url($path): string
{
$assetUrl = $this->assetExt->getAssetUrl($path);
return '#public_path/' . $assetUrl;
}
public function add_html_attr(string $html, $attr_name, $attr_value): string
{
// We assume a top level tag, on which will add an attribute
// value (without checking for a prior existence of the attribute key).
// We could use a naive Regex or a DOM library.
// https://github.com/scotteh/php-dom-wrapper#attr
// Or just the plain PHP DOMDocument() https://www.php.net/domdocument.loadhtml.
$doc = new \DOMWrap\Document();
$doc->setLibxmlOptions(LIBXML_HTML_NOIMPLIED);
return $doc
->html($html)
->children()->first()
->attr($attr_name, $attr_value);
}
public function add_class(string $html, $class_value): string
{
return $this->add_html_attr($html, 'class', $class_value);
}
public function getName()
{
return 'svg.extension';
}
}
#public_path is declared in config/packages/twig.yaml:
twig:
paths:
"%kernel.project_dir%/public": public_path
It is important to note you can have a near equivalent solution to target the SVG with CSS, without the overhead: just add an HTML tag wrapper. For eg.
<div class="svg-wrapper">
{{ source('#public_path'~asset('build/images/your_file.svg')) }}
</div>

Silex: translation not working in Twig

Currently, I develop a website by using the PHP micro-framework Silex. Now I try to use "TranslationServiceProvider" to translate my website into different languages. To achieve this, I have set the "locale" parameter :
$app->register(new Silex\Provider\TranslationServiceProvider(), array(
'locale' => 'pt'
));
Then, in my controller, I call the function "setLocale", like this:
$app['translator']->setLocale('it');
Now, if I display, always in my controller, the result of the translation its works fine:
$app['translator']->trans("hello"); // return "Buongiorno"
$app['translator']->getLocale(); // return "it"
But, if I call the same function in my template Twig, translation does not work:
{{ app.translator.trans('hello') }} // return: "Olá"
{{ app.request.locale }} // return: "pt"
So, I don't understand: translation works fine in my controller but when I want access translations in Twig, nothing happens.
Do you have any idea about what's going on?
Finally, I've found a solution to resolve my problem. In my "app.php" file, I have added the following code :
$app->before(function () use ($app) {
if ($locale = $app['request']->get('lang') or $locale = $app['request']->getSession()->get('_locale')) {
$app['locale'] = $locale;
$app['request']->setLocale($locale);
}
});
Then, I've written a function in my controller to change language:
public function changeLanguageAction(Request $request, Application $app, $language)
{
$app['request']->getSession()->set('_locale', $language);
return $app->redirect($app["url_generator"]->generate('index'));
}
Now, when I call "changeLanguage" function, all the translations work fine.
I don't know if this solution is a good practice but it works...

Twig tag or function inside PHP String

Is there any way to use something like this?
$foo = "{{ object|filter }}";
Because I'm trying to write a dynamic image converter that needs to output something like the example, but when in my twig I use {{ foo }}, it just outputs a raw string {{ object|filter }} instead of executhing the filter on the object as intended.
I've tried to use {{ foo | raw }} but same result.
What I'm trying to do exactly
CONTROLLER
$image = $em->getRepository('AcmeDemo:Media')->find($id);
$image_src = sprintf("{{ %s | imagine_filter('%s') }}", $image->getWebPath(), 'front_small');
return $this->render('image.html.twig', array(
'image_src' => $image_src
));
TWIG
<img src="{{ image_src }}"/>
So, I have a twig function inside a PHP variable $image_src, that Twig function could be, once formatted with sprintf {{ '/uploads/foo.jpg' | imagine_filter('front_small') }}.
That is a string for now, because it's inside a php variable $image_src, that variable is sent to my Twig template with the name image_src so, for now it is a string as I've said, if I do
My variable contains "{{ image_src }}" It will output a string that says:
My variable contains "{{ '/uploads/foo.jpg' | imagine_filter('front_small') }}"
Because, as I've said, image_src is just a string, but I want to acutally execute inside my Twig, the string that contains image_src, because yes, it is a string (to the eyes of the compiler) but we all know it is or it is pretended to be a Twig function (because of the syntax).
So, why | raw will not work?, because it is inteded to be used with strings containing HTML code, if it were HTML syntax it would work, but it's a Twig syntax, so It doesn't work.
Resuming, there should be a | compile twig function that executes Twig code inside a variable like | raw does with HTML, but, as this function doesn't exists, I'm wondering if there's a way to achieve it...
As #joshua said, it's like a Javascript eval.
I hope I've explained good what is the problem and what I need.
EDIT
I've used my own twig extension Compile in order to achieve what I needed.
class CompileExtension extends \Twig_Extension
{
public function getFilters()
{
return array(
'compile' => new \Twig_Filter_Method($this, 'compile', array(
'needs_environment' => true,
'needs_context' => true,
'is_safe' => array('compile' => true)
)),
);
}
public function compile(\Twig_Environment $environment, $context, $string)
{
$loader = $environment->getLoader();
$compiled = $this->compileString($environment, $context, $string);
$environment->setLoader($loader);
return $compiled;
}
public function compileString(\Twig_Environment $environment, $context, $string)
{
$environment->setLoader(new \Twig_Loader_String());
return $environment->render($string, $context);
}
public function getName()
{
return 'compile';
}
}
UPDATE
Accepting #Benjamin Paap answer because it does exactly what I wanted in this case with better code, but my custom Twig class works for every situation.
What you want to do is not possible in twig without a TwigExtension which renders your string separately.
But looking at your code you're trying to use the LiipImagineBundle the wrong way. It seems tempting to use it this way, but the correct way to generate a url for your thumbnails would be this:
class MyController extends Controller
{
public function indexAction()
{
// RedirectResponse object
$imagemanagerResponse = $this->container
->get('liip_imagine.controller')
->filterAction(
$this->request, // http request
'uploads/foo.jpg', // original image you want to apply a filter to
'my_thumb' // filter defined in config.yml
);
// string to put directly in the "src" of the tag <img>
$cacheManager = $this->container->get('liip_imagine.cache.manager');
$srcPath = $cacheManager->getBrowserPath('uploads/foo.jpg', 'my_thumb');
// ..
}
}
https://github.com/liip/LiipImagineBundle#using-the-controller-as-a-service

Using Twig to generate JSON

I want to have a URL that returns a simple JSON object. I am trying to use Twig to generate the JSON object:
{
"urls": [
{% for child in page.root %}
"{{ child.url }}"{% if not loop.last %},{% endif %}
{% endfor %}
]
}
The carriage returns will not remain in place though, and I keep getting a result that looks like this:
{'urls':['../ants/','../brick-report/','../the-pollution-intervention/','../barclay/','../broken-advertising/','../aldat-n-densom/','../thisisart/','../there-she-goes-again/']}
which Jquery will not parse with it's ajax or getJSON methods. It's totally ignoring this JSON. How might I convince Twig to put the right whitespace in place? I've looked at the manual and it only seems concerned with NOT inserting whitespace.
This works for me (twig template):
var parsedJSON = JSON.parse('{{ ['one', 'two', 'three']|json_encode|e('js') }}');
And this:
console.log(parsedJSON);
outputs:
Array ["one", "two", "three"]
in browser's console.
Twig has a filter for this.
json_encode, it uses PHP json_encode function.
for your case:
{{ {'urls': page.root}|json_encode }}
will output
{"urls":["..\/ants\/","..\/brick-report\/","..\/the-pollution-intervention\/","..\/barclay\/","..\/broken-advertising\/","..\/aldat-n-densom\/","..\/thisisart\/","..\/there-she-goes-again\/"]}
the code is tested and works. For more information take a look at the Twig Documentation for json_encode.
Generally it would make more sense to make controller return json directly, by returning JsonRespnse object
But if you really need to output JSON in Twig and assign it to variable, you can also use:
let foo = {{ bar|json_encode|raw }}
Don't use Twig to generate your json response.
In your controller, use:
return new Response(json_encode($var));
Sample:
public function sampleAction()
{
$urls = array('../test', '../something', '../sample');
return new Response(json_encode($var));
}
If URLs are generated from Symfony2 routes, you can use:
public function sampleAction()
{
$urls = array(
$this->generateUrl('my_test'),
$this->generateUrl('my_something'),
$this->generateUrl('my_sample'),
);
return new Response(json_encode($var));
}
Try wrapping your template in an autoescape block:
{% autoescape 'js' %}
{ "href": "{{ my_url }}" }
{% endautoescape%}
Thats easy if you extend twig.
First, create a class that will contain the extension:
<?php
namespace Acme\DemoBundle\Twig\Extension;
use Symfony\Component\DependencyInjection\ContainerInterface;
use \Twig_Extension;
class VarsExtension extends Twig_Extension
{
protected $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function getName()
{
return 'some.extension';
}
public function getFilters() {
return array(
'json_decode' => new \Twig_Filter_Method($this, 'jsonDecode'),
);
}
public function jsonDecode($str) {
return json_decode($str);
}
}
Basically the $.getJson() method requires json but ther is a string
so you can use $.get() to get the response and use the parser to parse the string to JSON
$.get("ffff",function(data){
// user parser
JSON.parse();
});

Categories