Is it possible to embed twig in Sonata Adminform field options? - symfony

Is it possible to add twig directives to field options in a Sonata Admin field option?
$fileFieldOptions = array('required' => false, 'data_class' => null);
if ($image && ($webPath = $image->getS3PathToImage())) {
// get the container so the full path to the image can be set
$container = $this->getConfigurationPool()->getContainer();
$fullPath = 'http://'.$webPath;
// add a 'help' option containing the preview's img tag
//$fileFieldOptions['help'] = '<img src="'.$fullPath.'" class="admin-preview" />';
$fileFieldOptions['help'] = "<img src='{{ '/relative/path/to/image.jpg' | imagine_filter('gi_thumb') }}' />";
I tried safe => true as suggested here but it does not seem to work with form fields.

Related

How to add image to node programmaticly?

There is an entity in which I added the Image field. The database has a custom table with different columns, including the id of the images.
Previously, I created a batch that writes data from this table to entity fields. That is, it creates many entities from the records in the table with filled fields. I need to do the same but for images. Part of the code from the batch:
if (empty($entity_id)) {
$info = [
'type' => 'product',
'title' => $productTitle,
'field_name' => (string) $product->name,
'field_product_cid' => (string) $product->cid,
'field_custom_url' => $product->url,
'uid' => 1,
// here I need to add an image to the field_image field from the table
];
$node = $this->entityTypeManager->getStorage('node')->create($info);
$node->save();
}
else {
$storage = $this->entityTypeManager->getStorage('node');
$node = $storage->load($entity_id);
// Change fields of node.
$node->set('title', $productTitle);
$node->set('field_name', (string) $product->name);
$node->set('field_custom_url', $product->url);
// and here change field_image if the node already exists
$node->save();
}
}
Someting like this:
$node = $storage->load($entity_id);
$image_source_path = '/some/path'
$image_target_directory = 'public://some/path';
$image_data = file_get_contents ($image_source_path);
$image_alt = 'some alt text';
// Drupal 9 >= 9.3.0 or Drupal 10
$image_object = \Drupal::service('file.repository')
->writeData($$image_data, $image_target);
// Drupal 8
//$image_object = file_save_data ($image_data, $image_target);
$node->set('field_image', [
'target_id' => $image_object->id(),
'alt' => $image_alt,
]);
$node->save();

Is it possible to upload a custom image or use the default image provided by a Drupal Theme, such as for the Logo Image?

I've made a custom theme for Drupal 8. Now I want to provide a banner image for the front page. I wouldn't like to create a custom content type Banner, then create a Banner-node and promote it to the front page, because the promoted node-teasers are arranged inside a grid-view in my theme (together with other node-teasers of other content types, such as Article) and when I do it I do not get what I expect. So it is totally not what I would like to do.
So first I decided to provide a custom banner_image field inside my_custom_theme.theme file (by implementing the appropriate hooks):
function my_custom_theme_preprocess_page(&$variables)
{
$variables['banner_image'] = theme_get_setting(‘banner_image’);
}
function my_custom_theme_form_system_theme_settings_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state)
{
$form['my_custom_theme_settings']['website'] = array(
'#type' => 'details',
'#title' => t('Website Settings'),
);
$form['my_custom_theme_settings']['website']['banner_image'] = array(
'#type' => 'managed_file',
'#upload_location' => 'public://media/branding/',
'#upload_validators' => array(
'file_validate_extensions' => array('gif png jpg jpeg'),
),
'#default_value' => theme_get_setting('banner_image'),
);
}
And I've got following result (Admin Panel):
It means: uploading a banner image works. However, I think I have done everything to get the image on my front page, without any success (the banner image doesn't show up).
Therefore I overrode the page--front.html.twig file:
<...>
{{ banner_image }}
<...>
No image, no Url, nothing (The image has been correctly uploaded to the appropriate location, the theme has been uninstalled, the cache has been cleared, and the theme has been reinstalled again).
What am I doing wrong? If it's impossible to do it that way, is there a way just to copy the same functionality of logo_image and use it for my banner_image?
1) By defining the banner_image settings in the install\my_theme_settings.yml file:
features:
logo: true
banner: true
2) By initializing a checked checkbox, such as for logo_image:
3) By showing the upload-fields, such as for logo_image, when the checkbox is unchecked:
I could solve it this way:
1) my_custom_theme.theme file:
function my_custom_theme_preprocess_page(&$variables)
{
$variables['banner_image_url'] = theme_get_setting('banner_image_url');
$variables['banner_image'] = my_custom_theme_get_banner_image_content();
}
function my_custom_theme_get_banner_image_content()
{
$bannerImage = theme_get_setting('banner_image_path', 'my_custom_theme');
$path = '';
if (!empty($bannerImage)) {
$file = file_load($bannerImage[0]);
if (!empty($file)) {
$uri = $file->getFileUri();
$path = file_create_url($uri);
}
}
if (empty($path)) {
$path = base_path() . drupal_get_path('theme', 'my_custom_theme') . '/' . theme_get_setting('banner_image_path', 'my_custom_theme');
}
$content = '<img alt="Banner Image" src="' . $path . '"/>';
return $content;
}
function my_custom_theme_form_system_theme_settings_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state)
{
$form['my_custom_theme_settings']['website'] = array(
'#type' => 'details',
'#title' => t('Website Settings'),
);
$form['my_custom_theme_settings']['website']['banner_image_url'] = array(
'#type' => 'textfield',
'#title' => t('Banner Image Url'),
'#default_value' => theme_get_setting('banner_image_url'),
);
$form['my_custom_theme_settings']['website']['banner_image_path'] = array(
'#type' => 'managed_file',
'#title' => t('Banner Image'),
'#default_value' => theme_get_setting('banner_image_path'),
'#upload_location' => 'public://media/branding',
);
}
2) config\install\my_custom_theme.settings.yml:
banner_image_url: '#'
banner_image_path: 'banner-image.jpg'
3) Inside the page.html.twig template:
{% if is_front and banner_image %}
<a href="{{ banner_image_url }}" class="banner-image">
{{ banner_image | raw }}
</a>
{% endif %}
you can make config form in custom module -src -Form -BannerConfigForm.php
$form['banner_image']= [
'#type' => 'managed_file',
'#required' => TRUE,
'#title' => $this->t('banner image'),
'#default_value' => $config->get('banner_image'),
'#upload_validators' => array(
'file_validate_extensions' => array('png gif jpg jpeg'),
),
'#upload_location' => 'public://'
];
and make menu link - module_name.links.menu.yml for add this as link in drupal admin setting
and in dolab.theme you can call it like that
function my_custom_theme_preprocess_page(&$variables)
{
$variables['banner_image_url'] = $file::load($config->get('banner_image')[0])->url();
}
and now you have this var banner_image_url you can use it in
<img src="{{banner_image_url}}" >
i wish that help you

Add placeholder or empty value to Entity Type with multiple = true in Symfony

I want to do this:
$builder->add('participants', EntityType::class, array(
'label' => 'Teilnehmer',
'class' => SchoolUser::class,
'multiple' => true,
'choices' => $this->getParticipantsOfEntry($builder),
'empty_value' => 'All',
'empty_data' => null,
'preferred_choices' => array(null)
));
But I get no selected 'All' - field at all. This should not be hard, I wonder where is my mistake?
'placeholder' = 'All',
did also not work for me.
How can I do this?
I know the question was 4-months ago but I ran into a similar problem so thought I would share my solution in case it helps others.
First in the Form object:
class MealFormType extends AbstractType {
public function buildForm( FormBuilderInterface $builder, array $options ) {
$builder->add( 'courses', EntityType::class, array(
'class' => MealCourse::class,
'multiple' => true,
) );
$builder->addEventListener( FormEvents::PRE_SUBMIT, function( FormEvent $event ) {
// Remove added entries here... maybe something like
$data = $event->getData();
if( ($key = array_search( 'all', $data['courses'] ) ) !== false ) {
unset( $data['courses'][$key] );
$event->setData( $data );
}
} );
}
public function configureOptions( OptionsResolver $resolver ) {
$resolver->setDefaults( ['data_class' => Meal::class] );
}
public function finishView( FormView $view, FormInterface $form, array $options) {
$newChoice = new ChoiceView( null, 'all', 'I want them all!' );
array_unshift( $view->children['courses']->vars['choices'], $newChoice );
}
}
This creates the form with multiple meal courses that can be selected, such as appetizer, soup, salad, entree, coffee, desert, ... using the buildForm() method. Also in this method an event listener for the PRE_SUBMIT event is added whose job it will be to remove any choices we have added since they are probably not real (if they were, we wouldn't have to add them in this painful way).
It sets the data_class as usual in configureOptions().
Lastly it provides the finishView() method that creates a new choice with value of 'all' that will display as 'I want them all' in the select, then adds it at the beginning for the 'choices' array for the 'courses' form entry (this would be $view->children['courses']->vars['choices'][] = $newChoice; to put it at the end).
Ok, this is fine but it really doesn't do anything more than having an additional option to click or unclick. To use this we need some Javascript to manage the form. I just stuck this Javascript in the bottom of my Twig file and use jQuery.
The Twig file:
{% block body %}
{{ form_start(mealForm) }}
{{ form_row(mealForm.courses,
{'attr': {'class': 'js-meal_course-select'}}) }}
<button type="submit">Save</button>
{{ form_end(mealForm) }}
{% endblock %}
{% block javascripts %}
{{ parent() }}
<script>
var mealCourseAllSelected = false;
jQuery(document).ready( function() {
// This will be called whenever an entry is selected or deselected
$('.js-meal_course-select').change( function() {
var selectedSet = $(this).val();
if( mealCourseAllSelected &&
(selectedSet.length != $(this).get(0).options.length - 1 ||
selectedSet.includes( 'all' )) ) {
var opts = $(this).get(0).options;
var len = opts.length;
for( var i = 0; i < len; ++i ) {
if( opts[i].value == 'all' ) opts[i].selected = false;
}
mealCourseAllSelected = false;
} else if( selectedSet.includes( "all" ) ||
selectedSet.length == $(this).get(0).options.length - 1 ) {
$('.js-meal_course-select option').prop( 'selected', true );
mealCourseAllSelected = true;
}
}
);
</script>
{% endblock %}
The JavaScript responds to changes to what is selected. If the select option with the value of 'all' is in the selected set, then all the select options are marked as selected, and a flag is set to say so. This is also done if everything is selected except the 'all' entry because that is kind of the definition of all.
If the 'all are selected' flag is set and there is a change, then that change must be deselecting at least one, unless that one is the select option with the value of 'all', it deselects the selection option with the value of 'all' and clears the 'all are selected' flag. If all are selected and the user tries to unselect just the select option with the value of 'all', it is just reselected.

Symfony2 Collection of Choises

I'm making my first Symfony web app.
Now after creating a couple of forms I found some problem creating a form with a collection of choises.
Creating a collection of TextTypes is very easy and is working in a couple of my forms. Now when I create a collections of ChoiseTypes it does not show anything on the screen. After checking the html code it only show the select statement without any options. Also the prototype contains only the select and not the options.
Where is how I add the collections of choises to the form:
->add('fieldTypes', CollectionType::class, array(
'entry_type' => ChoiceType::class,
'entry_options' => array(
'choices' => $fieldTypes,
'required' => true,
),
'allow_add' => true,
'allow_delete' => true,
'prototype' => true,
))
And this is the twig part of the sollution:
<td>
<ul id="typeTable" data-prototype="{{ form_widget(form.fieldTypes.vars.prototype)|e }}">
{% for fieldType in form.fieldTypes %}
<li>
{{ form_widget(fieldType) }}
</li>
{% endfor %}
</ul>
</td>
And the full JS code:
<script type="text/javascript">
// keep track of how many email fields have been rendered
var nameCount = '{{ form.fieldNames|length }}';
var typeCount = '{{ form.fieldTypes|length }}';
var optionCount = '{{ form.fieldOptions|length }}';
jQuery(document).ready(function () {
jQuery('#add-new-field').click(function (e) {
e.preventDefault();
var nameList = jQuery('#nameTable');
var typeList = jQuery('#typeTable');
var optionList = jQuery('#optionTable');
// grab the prototype template
var newNameWidget = nameList.attr('data-prototype');
var newTypeWidget = typeList.attr('data-prototype');
var newOptionWidget = optionList.attr('data-prototype');
//var newTypeWidget = jQuery('#fieldType_prototype');
// replace the "__name__" used in the id and name of the prototype
// with a number that's unique to your emails
// end name attribute looks like name="contact[emails][2]"
newNameWidget = newNameWidget.replace(/__name__/g, nameCount);
newTypeWidget = newTypeWidget.replace(/__name__/g, typeCount);
newOptionWidget = newOptionWidget.replace(/__name__/g, optionCount);
var $options = $("#fieldType_prototype > option").clone();
//$(newTypeWidget).append($options);
$("#form_fieldTypes_" + typeCount).html( $("#fieldType_prototype").html() );
// newTypeWidget.attr("id","form_fieldTypes_" + typeCount);
// newTypeWidget.attr('name', 'form[fieldTypes][' + typeCount + ']');
nameCount++;
typeCount++;
optionCount++;
// create a new list element and add it to the list
var newLi = jQuery('<li></li>').html(newNameWidget);
newLi.appendTo(nameList);
var newLi = jQuery('<li></li>').html(newTypeWidget);
newLi.appendTo(typeList);
var newLi = jQuery('<li></li>').html(newOptionWidget);
newLi.appendTo(optionList);
});
})
</script>
Variable $fieldTypes contains this:
array('Text Field' => TextType::class,
'Text area' => TextareaType::class,
'Email' => EmailType::class,
'Number' => IntegerType::class,
'Url' => UrlType::class,
'Drop down' => ChoiceType::class,
'Date' => DateType::class,
'Checkbox' => CheckboxType::class);
}
Can someone help me with this issue?
It was a bug:
xabbuh:
'I guess you might be affected by a regression that will be fixed by github.com/symfony/symfony/pull/17162. Can you test if the changes from that PR solve your problem?'

How to wrap field in custom markup?

Situation
I have a custom module that is using hook_field_formatter_info() to add an "fancy" option to the image field in the manage display of a content type. When this option is chosen I want to be able to surround the ENTIRE field in custom divs and markup. I want the solution to be a hook in my custom module and not a template override.
Process
A user wants the display of an image field to be the "fancy" option. They check it in the drop down and click save from the content types Manage Display admin page. Now on that content type node there should be custom markup surrounding the entire field even if there are multiple images, the new markup should surround ALL the images and not each individual image.
hook_field_formatter_view
hook_field_formatter_view seems to demand it's output be an array and I can't seem to be able to wrap the output in a div.
Template overrides
I can't just make a field.tpl.php for the specific field because like I initially mentioned I need to be able to switch themes and the module still work like normal. Unless there is a way to get my module to override any field where said field has hook_field_formatter_info() set specifically.
/**
* Implements hook_field_formatter_view().
*/
function bootstrap_modal_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array();
foreach ($items as $delta => $item) {
if ($index === NULL || $index === $delta) {
$element[$delta] = array(
'#theme' => 'bootstrap_modal_image_formatter',
'#item' => $item,
'#entity_type' => $entity_type,
'#entity' => $entity,
'#node' => $entity, // Left for legacy support.
'#field' => $field,
'#display_settings' => $display['settings'],
'#delta' => $delta,
);
}
}
// $element = '<div id="CUSTOMDIVSTUFF">' . $element . '</div>';
return $element;
}
And here is the #theme function:
function theme_bootstrap_modal_image_formatter($variables) {
$item = $variables['item'];
$entity_type = $variables['entity_type'];
$entity = $variables['entity'];
$field = $variables['field'];
$settings = $variables['display_settings'];
$image = array(
'path' => $item['uri'],
'alt' => isset($item['alt']) ? $item['alt'] : '',
'title' => isset($item['title']) ? $item['title'] : '',
'style_name' => $settings['bootstrap_modal_node_style'],
);
if (isset($item['width']) && isset($item['height'])) {
$image['width'] = $item['width'];
$image['height'] = $item['height'];
}
if (isset($item['attributes'])) {
$image['attributes'] = $item['attributes'];
}
// Allow image attributes to be overridden.
if (isset($variables['item']['override']['attributes'])) {
foreach (array('width', 'height', 'alt', 'title') as $key) {
if (isset($variables['item']['override']['attributes'][$key])) {
$image[$key] = $variables['item']['override']['attributes'][$key];
unset($variables['item']['override']['attributes'][$key]);
}
}
if (isset($image['attributes'])) {
$image['attributes'] = $variables['item']['override']['attributes'] + $image['attributes'];
}
else {
$image['attributes'] = $variables['item']['override']['attributes'];
}
}
$entity_title = entity_label($entity_type, $entity);
if ($style_name = $settings['bootstrap_modal_image_style']) {
$path = image_style_url($style_name, $image['path']);
}
else {
$path = file_create_url($image['path']);
}
$caption = 'some value';
$gallery_id = 'some value';
return theme('bootstrap_modal_imagefield', array('image' => $image, 'path' => $path, 'title' => $caption, 'gid' => $gallery_id));
}
Been working on this for days, I'm drowning here.
This is possible via HOOK_process_field(). Use this hook to define a new template file for your field by adding it to theme_hook_suggestions array (place the template file in your module directory, because you wanted to change the theme -as you mentioned-). More info about field template suggestions.
Then, you will need to add the module path to drupal theme registry, so it picks up the template file. Demo.
I found out it was:
function bootstrap_modal_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
$element = array(
'#prefix' => '<div class="wrapper">',
'#suffix' => '</div>',
);

Resources