Is it possible to set a series of global properties (such as social media usernames) that are available to all page views in OctoberCMS rather than having them associated to one CMS page or Static Page at a time?
For example, being able to use {{ twitter_username }} in any template, but it wouldn't show up as a field in any page form on the backend.
UPDATE: this can be achieved by registering a Twig function using registerMarkupTags in your plugin:
use System\Classes\PluginBase;
class Plugin extends PluginBase
{
public function registerMarkupTags()
{
return [
'functions' => [
'globals' => function($var) {
switch ($var) {
case 'twitter_username':
return 'mytwitterusername';
}
return null;
},
],
];
}
}
In this case, calling {{ globals('twitter_username') }} from any template prints mytwitterusername.
Hmm yes better you need to add code to life-cycle method in layouts, so now page which are using that layout will have this info already loaded.
In layout code block you can use something like this
use RainLab\Pages\Classes\Page as StaticPage;
function onStart() {
$pageName = 'static-test'; // this will be static page name/filename/title
$staticPage = StaticPage::load($this->controller->getTheme(), $pageName);
$this['my_title'] = $staticPage->viewBag['title'];
$this['twitter_username'] = $staticPage->viewBag['twitter_username'];
}
now inside your cms page you can use this variable
<h1>{{ my_title }} </h1>
<h3>{{ twitter_username }} </h3>
let me know if it you find any issues
You could also use theme config file which gives you more flexibility rather than hardcoding the values in to the code block.
https://octobercms.com/docs/themes/development#customization
Related
I'm using Symfony 6.1 with Twig on PHP 8.1. I've already set up my complete layout including fragments/partials/components or whatever we call reusable pieces of the layout at the moment. These fragments include certain kinds of navigations, quotes, tabs and so on which are always supposed to look and work the same and usually only receive a variable with the same name as an array with a few options.
This is how I'm embedding a YouTube-Video for example:
{% set youtube_video = { 'id': 'dQw4w9WgXcQ', 'title': 'Secret Video (unlisted)', 'language': 'en' } %} {% include 'fragment/youtube_video.html.twig' %}
Most of these fragments can be used multiple times on one page (= in the main template of the view or within the base template/s). Some of them however are supposed to be used only once and using them multiple times would create layout issues (e.g. a navigation for mobile devices with a specific CSS id).
For other fragments I would like to have a counter to add a CSS id in addition to a normal class:
<div class="fragment_video" id="fragment_video_{{ counter }}> ... </div>
The question now is how I can count within a fragment template like 'fragment/youtube_video.html.twig' how often this template has been used in that page already. I don't see any Twig functions or anything within the "app" variable for that.
Now I could create a custom Twig function "counter" and call that with with a unique name:
<div class="fragment_video" id="fragment_video_{{ counter('fragment_video') }}> ... </div>
or
{% if counter('fragment_video') == 1 %} ... {% endif %}
BUT how would I store the current count per given name? I don't want to use $GLOBALS in Twig or rather Symfony and storing that information in the session would keep it past the current request. Is there another solution available?
This is how it would look like as a Twig function:
public function getCounter(string $name): int
{
$name = 'twig_counter_'.$name;
if (isset($GLOBALS[$name])) {
++$GLOBALS[$name];
} else {
$GLOBALS[$name] = 1;
}
return $GLOBALS[$name];
}
As suggested by #DarkBee the instance of a TwigExtension object can use properties to keep track of some information:
/**
* #var array<int>
*/
private array $count = [];
...
public function getCounter(string $name): int
{
if (isset($this->count[$name])) {
++$this->count[$name];
} else {
$this->count[$name] = 1;
}
return $this->count[$name];
}
I try to set up a simple CMS on our web application using the Symfony CMF.
I can successfully load the fixtures in multiple languages.
$parent = $dm->find(null, '/cms/pages');
$rootPage = new Page(array('add_locale_pattern' => true));
$rootPage->setTitle('main');
$rootPage->setParentDocument($parent);
$rootPage->setName('main');
$rootPage->setBody('');
$dm->persist($rootPage);
$aboutPage = new Page(array('add_locale_pattern' => true));
$aboutPage->setTitle('About');
$aboutPage->setParentDocument($rootPage);
$aboutPage->setBody('About us DE');
$aboutPage->setName('about');
$aboutPage->setLabel('About');
$dm->persist($aboutPage);
$dm->bindTranslation($aboutPage, 'de');
$aboutPage->setBody('About us FR');
$aboutPage->setLabel('About FR');
$dm->bindTranslation($aboutPage, 'fr');
I can also display them in the right language (current locale) on the front page.
This is my controller action:
public function pageAction(Request $request, $contentDocument) {
return $this->render(':Frontend/CMS:index.html.twig', ['page' => $contentDocument]);
}
And this is my working twig File:
{{ page.body }}
Screenshot of the working page
But as soon as I try to render a menu on my page, it will show the text in the default language.
{{ knp_menu_render('main') }}
{{ page.body }}
Screenshot of the non working page
The menu is configured as follow:
cmf_menu:
persistence:
phpcr:
menu_basepath: /cms/pages
The output of app.request.locale is always fr. No matter if I include the menu or not.
Does anyone have an idea what could cause this problem?
I am trying to use a View composer to automatically modify page titles based on a defined section.
#section('title', 'Page')
And use it like this:
<title>{{ $title }}</title>
I've written the following code as the composer, but it doesn't work correctly. It just displays the title as Website - Website when it should say Page - Website or, in the case of not including a title, Website.
View::composer('*', function($view){
$title = $view->title;
$view->with('title', !empty($title) ? $title . " - Website" : "Website");
});
This only needs to work on one template, master, but when I replaced '*' with 'master' it didn't modify the behaviour at all.
What do I need to change to make this correctly modify the title section?
You can use
view()->composer('*', function ($view) {
$view->with('title', 'Your title');
});
In service provider.
Or you can use share
view()->share('key', 'value');
Then in view use
{{ $key }}
May be try to use this:
view()->composer('*', function ($view) {
$data = $view->getData();
//here you can get your data which was sent to view
//exmp. $old_title = $data['title'];
//where 'title' - key of variable which sent via ->with('title','data')
$view->with('title', 'new title');
});
I am beginner in Phalcon framework, I have to validate form elements in .volt page
I have one form class file where I write hidden filed for record edit purpose, I'm storing record's id in hidden filed when its in edit mode
if ($options["edit"] == 1) {
// $tax_categories_id = new Hidden("tax_categories_id");
$this->add(new Hidden('tax_categories_id'));
//$this->add($tax_categories_id);
}
The problem is when I rendering this hidden filed in add.volt
{{ form.render('tax_categories_id')}}
Its working fine in time of edit mode, but in new record time its give error
Phalcon\Forms\Exception: Element with ID=tax_categories_id is not a part of the form
I know the why error is coming but i am not able to validate this field in .volt file
In the controller can you set your $options variable and then check for it inside of the view?
//controller.php
$this->view->setVar('options', $options);
//view.volt
{% if options['edit'] %}
{{ form.render('tax_categories_id')}}
{% endif %]
Just check if the element is exist
// add.volt
{% if form.has('tax_categories_id') %}
{{ form.render('tax_categories_id') }}
{% endif %}
Assuming you have crated something close to:
<?php
use Phalcon\Forms\Form,
Phalcon\Forms\Element\Text,
Phalcon\Forms\Element\Hidden;
class UsersForm extends Form
{
public function initialize($options = [])
{
if ( isset($options['edit']) && $options['edit'] ) {
$this->add(new Hidden('id'));
}
$this->add(new Text('name'));
}
}
So! Depending on options, you may have one field declared, or two instead. Now when someone sends you this form back, for validation you have to set it up again with proper $options['edit'], depending on if you have $_REQUEST['id'] declared or not:
$form = null;
if( isset($_REQUEST['id']) ) {
$form = new UsersForm();
} else {
$form = new UsersForm(['edit' => true]);
}
$form->bind($_REQUEST);
if($form->isValid()) {
//...
}
Quite an advanced (but with some gaps anyway) manual is here. Bet you were there already, but just in case.
Btw, form are iterators & traversables, so you can loop over them to render only elements, that are declared. Writing this because you have put {{ form.render('tax_categories_id')}} as an example and that makes me feel like you are generating fields by hand.
I have a variable suppose that is:
$menustr; this variable contains code html and some twig parts for example:
$menustr .= '<li><a href="{{ path("'. $actual['direccion'] .'") }}" >'. $actual['nombre'] .'</a></li>';
I need that the browser take the code html and the part of twig that in this momen is the
"{{ path(~~~~~) }}"
I make a return where i send the variable called "$menustr" and after use the expresion "raw" for the html code but this dont make effective the twig code.
This is te return:
return $this->render('::menu.html.twig', array('menu' => $menustr));
and here is the template content:
{{ menu | raw }}
Twig can't render strings containing twig. There is not something like an eval function in Twig1..
What you can do is moving the path logic to the PHP stuff. The router service can generate urls, just like the path twig function does. If you are in a controller which extends the base Controller, you can simply use generateUrl:
$menuString .= '<li>'. $actual['nombre'] .'</li>';
return $this->render('::menu.html.twig', array(
'menu' => $menuString,
));
Also, when using menu's in Symfony, I recommend to take a look at the KnpMenuBundle.
EDIT: 1. As pointed by #PepaMartinec there is a function which can do this and it is called template_from_string
You can render Twig template stored in a varible using the template_from_string function.
Check this bundle: https://github.com/LaKrue/TwigstringBundle
This Bundle adds the possibility to render strings instead of files with the Symfony2 native Twig templating engine:
$vars = array('var'=>'x');
// render example string
$vars['test'] = $this->get('twigstring')->render('v {{ var }} {% if var is defined %} y {% endif %} z', $vars);
// output
v x y z
In your case i would be:
return $this->render('::menu.html.twig', array(
'menu' => $this->get('twigstring')->render($menustr, $vars)
));