NoReverseMatch when using Apphook without namespace - django-cms

I use Django CMS 3 and Django 1.6 and the default django polls app , and I am doing this short tutorial.
My problem is that the PollsApp works fine when it's using namespace like this:
djangocms_polls/cms_app.py:
...
class PollsApp(CMSApp):
name = _("Poll App")
urls = ["polls.urls"]
app_name = "polls"
...
polls/templates/polls/index.html:
...
{% for poll in latest_poll_list %}
<li>{{ poll.question }}</li>
{% endfor %}
...
But when I delete the "polls:" part from the index.html, it won't work (and it doesn't matter if there is or isn't app_name field in PollsApp) and I get this Error:
NoReverseMatch at /polls/
Exception Value:
Reverse for 'detail' with arguments '(1L,)' and keyword arguments '{}' not found. 0 pattern(s) tried: []
Why am I making my life hard when everything works - you ask? It's because I want to use apps that doesn't use namespaces like django-shop and when I created apphook for django-shop - the same problem occured.

When you're using apps via an Apphook in CMS you have to supply a namespace to when creating URLs in the templates.
In general also, when creating apps I believe it to be from a 'best practice' approach to always namespace your app and template URLs.
If you're using an app which doesn't make use of the app_name Meta attribute, you can define a namespace when you include an app's URLs in your root urls.py. Take a look over the example here; https://docs.djangoproject.com/en/1.7/topics/http/urls/#reversing-namespaced-urls

Related

Translation of flash messages with parameters in Symfony 4

I have a problem with flash messages in Symfony 4 and translation.
Translation of simple flash messages is working fine:
$this->addFlash('success', 'flashmessage.project_deleted');
But now I want to add some parameters to the flash messages and I have no idea how to handle it. I tried a lot, but nothing is working. I want to show in the flash messages the title of projects after f.e. removing. For example:
$this->addFlash('success', sprintf('flashmessage.project_deleted: %s', $project->getTitle()));
But the translation is not recognized, because the parameter is replaces before translation happens (I think so). And it should also be possible to have parameters in the middle of a string and not only at the end or at the beginning and ideally more than one parameter.
I'm using this in my Controller which extends AbstractController.
Does anybody has a solution for this?
Usually you would pass in the parameters to the translation, so your code snippet should probably look your first example and then in twig you would have something like this:
{% for message in app.flashes('success') %}
<div class="alert alert-success">
{{ message|trans({ 'title': project.title }) }}
</div>
{% endfor %}
The translation then should contain the parameter that is replaced:
flashmessage:
project_created: 'The project "%title%" was created successfully.'
project_deleted: 'You successfully deleted the project "%title%".'
...
Obviously the downside is that you have to dynamically pass in the variables which does not make much sense for flash messages, as not all of them will require these parameters. Also, as you already mentioned, when you deleted the project you will probably not have it available anymore in the template.
Instead I would recommend translating the message before passing it into the flash bag:
$this->addFlash(
'success',
$this->translator->translate(
'flashmessage.project_deleted',
[
'title' => $project->getTitle(),
]
)
);
This will require that you pass in the translator to your controller. You could either create your own base controller similar to Symfony's AbstractController for this and create something like a $this->trans()-method to make it easier to translate things inside your controller. Also, you will still have to make sure that $project->getTitle() will still return a value, so you probably want to call this before you actually delete the entry or have the data in memory.
When you do it this way, then you should not translate the flash messages in the template itself because they are already translated. This will still work because when Symfony tries to translate the already translated message, e.g. You successfully deleted the project "foo". then it will not find a translation and just print the original text instead, but you will get warnings in your logs about missing translations. The solution is to remove the |trans in your template (see first snippet).
A possible solution is to add another flash with serialized parameters.
Then, when you display your flash message, check if that extra flash exists and, if so, deserialize it and use it as argument.
Example follows.
In controller:
$this->addFlash('success', 'flashmessage.project_deleted');
$this->addFlash('_params', serialize(['%project%' => $project->getTitle()]));
In template:
{% flashMessage = app.session.flashbag.get('info') %}
{% if app.session.flashbag.has('_params') %}
{% set flashParams = app.session.flashbag.get('_params')|first|unserialize %}
{{ flashMessage|trans(flashParams) }}
{% else %}
{{ flashMessage|trans }}
{% endif %}
You need to create a Twig extension that defines an unserialize filter (or use a library that provides it)
Since Symfony 5.2 you can use the TranslatableMessage object to achieve this.
https://symfony.com/doc/current/translation.html#translatable-objects
use Symfony\Component\Translation\TranslatableMessage;
$this->addFlash(
'success',
new TranslatableMessage(
'flashmessage.project_deleted',
['%project%' => $project->getTitle()]
)
);
Then in your Twig template you only need to use {{ flashMessage|trans }}.
This works without injecting the Translator service, or messing about with anything in Twig.
Have a look at the ICU Message Format: https://symfony.com/doc/current/translation/message_format.html

Is it possible to output message keys in Symfony

I have a Symfony 5 website that uses Twig templates containing messages translated with
{% trans %}some.message.key{% endtrans %}
Is there a way to have Symfony output the message key itself instead of the translation? This can be helpful during development and translation work.
To demonstrate what I want, have a look at https://en.wikipedia.org/wiki/Main_Page?uselang=qqx, which shows such behavior in the MediaWiki software.

reading yaml from twig

Preface: in ez4 i remember there was a tpl function to read ini settings, we used to use this to pass specific locations or id's with which we could then render certain content.
In ezplatform I am now doing the same thing but by using the PreContentViewListener (in the PreContentViewListener read a yml file and pass into the view as params), but this doesn't feel like the correct way as the PreContentViewListener doesn't always get triggered, in custom controllers for example.
Question
Is there a native way to read yaml files from within twig templates? After searching the docs and available packagists i cannot find anything :/
If your needs are simple (i.e. reading container parameters), you can also use eZ Publish config resolver component which is available in any Twig template with ezpublish.configResolver.
You can specify a siteaccess aware parameter in format <namespace>.<scope>.<param_name>, like this:
parameters:
app.default.param.name: 'Default param value'
app.eng.param.name: 'English param value'
app.cro.param.name: 'Croatian param value'
where default, eng and cro are different eZ Publish scopes.
You can then use the config resolver to fetch the parameter in current scope with:
{{ ezpublish.configResolver.parameter('param.name', 'app') }}
If you have Legacy Bridge installed, this even falls back to legacy INI settings if no Symfony container parameter exists:
{{ ezpublish.configResolver.parameter('SiteSettings.SiteName', 'site') }}
Disclaimer: Some say that using config resolver is bad practice, but for simpler usecases it is okay, IMO.
Have a look to our CjwPublishToolsBundle.
https://github.com/cjw-network/CjwPublishToolsBundle
https://github.com/cjw-network/CjwPublishToolsBundle/blob/master/Services/TwigConfigFunctionsService.php
Here we have 2 wrapper twig functions
{{cjw_config_resolver_get_parameter ( 'yamlvariablename', 'namespace default ezsettings') }}
=> ezpublish siteaccessmatching
{{cjw_config_get_parameter( 'mailer_transport' )}}
=> core symfony yaml reader without siteaccess
You could do a lot of things in eZ 4 and not always really good for your application design. ezini was able to read the configuration from the template but now in eZ Platform and by extension Symfony you need to respect more common patterns. IMO the view should not be that smart.
Then injecting variables to the view from a listener (PreContentViewListener or your own) is not a bad idea.
You can also use the Twig Globals that could allow you to do 2 global things:
inject variables (1)
inject a service (2)
Look here: https://symfony.com/doc/current/templating/global_variables.html
(2): please don't inject the service container globally it is bad
(1): I don't remember if the Twig Globals are Site Access aware, if not injecting your own service (2) to manage access to the config might be better.
And finally, I think that the use case is not a common one:
we used to use this to pass specific locations or id's with which we could then render certain content.
Most of the time it is a bad idea to pass ids coming from the configuration to render something, it is much better to organize the content structure to let you pull the location you want using the PHP API. (no id in configuration no hassle with dev, stage, preprod and prod architecture)

Pluralize string in Symfony 3

Are there any ways for pluralizing a string in Symfony 3? For example: I have "Object" and I would like to get "Objects".
pluralization is a very complex topic and Symfony embraces it as part if the Translation Component:
Message pluralization is a tough topic as the rules can be quite complex.
So you basically would need to activate translations for your system and translate the needed strings using the transChoice() method of the translator service or the 'transchoice' tag / filter in your twig template.
To handle this, use the transChoice() method or the transchoice tag/filter in your template.
using the translator service
// the %count% placeholder is assigned to the second argument...
$translator->transChoice(
'There is one apple|There are %count% apples',
10
);
// ...but you can define more placeholders if needed
$translator->transChoice(
'Hurry up %name%! There is one apple left.|There are %count% apples left.',
10,
// no need to include %count% here; Symfony does that for you
array('%name%' => $user->getName())
);
twig tags
{% trans %}Hello %name%{% endtrans %}
{% transchoice count %}
{0} There are no apples|{1} There is one apple|]1,Inf[ There are %count% apples
{% endtranschoice %}
UPDATE
If you do not know the string beforehand you can use the internal Inflector Component, but be aware of the disclaimer and the fact that this would only work for strings in English:
This component is currently marked as internal. Do not use it in your own code. Breaking changes may be introduced in the next minor version of Symfony, or the component itself might even be removed completely.
An alternative would be to create your own inflector class, e.g. something like this and create a service from it.

In Jekyll, can we group multiple collections inside the same folder?

In config.yml, I define my collections like this:
collections:
music:
output: false
dancing:
output: false
The problem is I will have lots of collections and they will clutter my root Jekyll folder.
Is there a way to group all the collections into a folder, named for example, _collections?
So, I would have:
_collections
_dancing
_music
....
This is now possible (I'm running Jekyll 3.7.2, I'm not sure in which version this was implemented).
Here's how to do it: In your _config.yml you can define your collections as well as the folder for your collections. Let's take a look at an example on a client site I'm working on:
collections:
events:
output: true
work:
output: true
jobs:
output: true
cases:
output: true
permalink: /work/:name
collections_dir: pages
The collections_dir: [your_folder_here] will tell Jekyll to look into that folder for collections. My folder structure in development is as follows:
pages/
...
_events/
_work/
_jobs/
_cases/
And in the compiled site it's as follows:
...
events/
jobs/
work/ (contains both "work" and "cases" collections)
One thing also that wasn't asked, but I found to be useful, was that you're able to output different collections into a same folder. In my case I had a client website on which there were two types of work samples: client cases and general examples. We wanted to separate them for better maintenance but also show them in the same folder. To achieve this you can simply define a permalink for the collection. In our case we put permalink for the cases to appear in the work folder (permalink: /work/:name).
Hope this helps!
This is also present in the Jekyll documentation
Answer is no. Your collections folder must be at the root of your root folder.
Even if you name you create a collection in _collections/_music folder, and set it up like this :
collections:
collections/_music folder:
output: true
Jekyll ends up looking for your collection in _collections_music folder (without any slash) because of path sanitize process.
See jekyll code in collection.rb, site.rb and jekyll.rb
This is now possible since this issue was merged.
User configures as:
collections_dir: my_collections
Then we look in my_collections/_pizza for the pizza collection, and my_collections/_lasagna for the lasagna collection.
Nothing prevents you to use subfolders in your collection.
(Note: this is not an answer to your question but a possible workaround)
So as a workaround you could have just one collection: say _arts for example and organize your folders like:
_arts
dancing
music
concerts
.....
to list them separately you can use:
a FrontMatter variable in your files category: dancing when you have a output and check this for a dancing only list for example.
{% for project in site.arts %}
{% if project.category == 'dancing' %}
....
{% endif %}
{% endfor %}
or check the path when you have no output for the collection
{% for project in site.arts %}
{% if project.url contains 'dancing' %}
....
{% endif %}
{% endfor %}
This could slowdown your build if you have hundreds and hundreds of items inside.

Resources