Is there a Drupal module to display the user name in a block, when he is logged in ?
thanks
Creat a new block.
Format: PHP code
Block Body:
<?
global $user;
print $user->name;
?>
In Drupal 7, using a custom module named YOURMODULE:
/**
* Implements hook_block_info().
*/
function YOURMODULE_block_info() {
return array(
'YOURMODULE_logged_in_as' => array(
'info' => t('Login information ("Logged in as...").'),
'cache' => DRUPAL_CACHE_PER_USER,
),
);
}
/**
* Implements hook_block_view().
*/
function YOURMODULE_block_view($delta = '') {
if ($delta == 'YOURMODULE_logged_in_as') {
global $user;
return array(
'subject' => NULL,
'content' => t('Logged in as !name', array('!name' => theme('username', array('account' => $user)))),
);
}
}
Related
I have this form: https://greektoenglish.com/translation
After I complete the form, provide it with a file, and finally submit it, I get this error: "field is required". That the file field is required. But I already completed the field.
If I remove "'#required' => TRUE," from the code where the file upload field is declared, fill the form out, and submit it, then the form is submitted correctly.
How can I solve this?
This is my code:
<?php
namespace Drupal\submit_translation\Form;
use Drupal\Component\Utility\EmailValidatorInterface;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\Mail\MailManagerInterface;
use Drupal\mimemail\Utility\MimeMailFormatHelper;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* The example email contact form.
*/
class SubmitTranslation extends FormBase {
/**
* The email.validator service.
*
* #var \Drupal\Component\Utility\EmailValidatorInterface
*/
protected $emailValidator;
/**
* The language manager service.
*
* #var \Drupal\Core\Language\LanguageManagerInterface
*/
protected $languageManager;
/**
* The mail manager service.
*
* #var \Drupal\Core\Mail\MailManagerInterface
*/
protected $mailManager;
/**
* Constructs a new ExampleForm.
*
* #param \Drupal\Component\Utility\EmailValidatorInterface $email_validator
* The email validator service.
* #param \Drupal\Core\Language\LanguageManagerInterface $language_manager
* The language manager service.
* #param \Drupal\Core\Mail\MailManagerInterface $mail_manager
* The mail manager service.
*/
public function __construct(EmailValidatorInterface $email_validator, LanguageManagerInterface $language_manager, MailManagerInterface $mail_manager) {
$this->emailValidator = $email_validator;
$this->languageManager = $language_manager;
$this->mailManager = $mail_manager;
}
/**
* {#inheritdoc}
*/
public static function create(ContainerInterface $container) {
return new static(
$container->get('email.validator'),
$container->get('language_manager'),
$container->get('plugin.manager.mail')
);
}
/**
* {#inheritdoc}
*/
public function getFormId() {
return 'submit_translation_form';
}
/**
* {#inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state, $dir = NULL, $img = NULL) {
$form['intro'] = [
'#markup' => $this->t('Use this form to send us the document that we\'ll translate!'),
];
$form['from'] = [
'#type' => 'textfield',
'#title' => $this->t('Name'),
'#description' => $this->t("Your full name."),
'#required' => TRUE,
];
$form['from_mail'] = [
'#type' => 'textfield',
'#title' => $this->t('Email address'),
'#description' => $this->t("Your email address."),
'#required' => TRUE,
];
$form['params'] = [
'#tree' => TRUE,
'subject' => [
'#type' => 'textfield',
'#title' => $this->t('Title'),
'#description' => $this->t("The title of the document."),
'#required' => TRUE,
],
'count' => [
'#type' => 'textfield',
'#title' => $this->t('Word Count'),
'#description' => $this->t("The word count of the document."),
'#required' => TRUE,
],
'body' => [
'#type' => 'textarea',
'#title' => $this->t('Comments'),
'#description' => $this->t("Tell us if you have any special requirements."),
'#required' => TRUE,
],
// This form element forces plaintext-only email when there is no HTML
// content (that is, when the 'body' form element is empty).
'plain' => [
'#type' => 'hidden',
'#states' => [
'value' => [
':input[name="body"]' => ['value' => ''],
],
],
],
'attachments' => [
'#name' => 'files[attachment]',
'#type' => 'file',
'#title' => $this->t('Choose a file to send for translation.'),
'#required' => TRUE,
],
];
$form['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Send message'),
];
return $form;
}
/**
* {#inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
// Extract the address part of the entered email before trying to validate.
// The email.validator service does not work on RFC2822 formatted addresses
// so we need to extract the RFC822 part out first. This is not as good as
// actually validating the full RFC2822 address, but it is better than
// either just validating RFC822 or not validating at all.
$pattern = '/<(.*?)>/';
$address = $form_state->getValue('from_mail');
preg_match_all($pattern, $address, $matches);
$address = isset($matches[1][0]) ? $matches[1][0] : $address;
if (!$this->emailValidator->isValid($address)) {
$form_state->setErrorByName('from_mail', $this->t('That email address is not valid.'));
}
$file = file_save_upload('attachment', [ 'file_validate_extensions' => array('doc docx pdf')], 'temporary://', 0);
if ($file) {
$form_state->setValue(['params', 'attachments'], [['filepath' => $file->getFileUri()]]);
}
}
/**
* {#inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// First, assemble arguments for MailManager::mail().
$module = 'submit_translation';
$key = "solon_key";
$to = "info#gexl.eu";
$langcode = $this->languageManager->getDefaultLanguage()->getId();
$params = $form_state->getValue('params');
$reply = "";
$send = TRUE;
$params['body'] .= " Count: " . $params['count'];
// Second, add values to $params and/or modify submitted values.
// Set From header.
if (!empty($form_state->getValue('from_mail'))) {
$params['headers']['From'] = MimeMailFormatHelper::mimeMailAddress([
'name' => $form_state->getValue('from'),
'mail' => $form_state->getValue('from_mail')
]);
}
elseif (!empty($form_state->getValue('from'))) {
$params['headers']['From'] = $from = $form_state->getValue('from');
}
else {
// Empty 'from' will result in the default site email being used.
}
// Handle empty attachments - we require this to be an array.
if (empty($params['attachments'])) {
$params['attachments'] = [];
}
// Remove empty values from $param['headers'] - this will force the
// the formatting mailsystem and the sending mailsystem to use the
// default values for these elements.
foreach ($params['headers'] as $header => $value) {
if (empty($value)) {
unset($params['headers'][$header]);
}
}
// Finally, call MailManager::mail() to send the mail.
$result = $this->mailManager->mail($module, $key, $to, $langcode, $params, $reply, $send);
if ($result['result'] == TRUE) {
$this->messenger()->addMessage($this->t('Your message has been sent.'));
}
else {
// This condition is also logged to the 'mail' logger channel by the
// default PhpMail mailsystem.
$this->messenger()->addError($this->t('There was a problem sending your message and it was not sent.'));
}
}
}
This happens because the form element '#type' => 'file' has no #value to validate. #required fields must have a #value set otherwise validation fails.
This is (now considered) a very old issue that has been fixed in Drupal 9.5.x, but this was assumed in the good old days of Drupal 7, as mentioned in the Form API reference :
#required: Indicates whether or not the element is required. This
automatically validates for empty fields, and flags inputs as
required. File fields are NOT allowed to be required.
So I guess the best solution is to upgrade to 9.5.x or above, if feasible, but as sometimes upgrading makes things complicated, you might prefer to review and apply the patch manually to your current code base.
[EDIT]: If still having issues after upgrade to >= 9.5.2,
Looking at the patch, a default valueCallback is now used to provide a #value to file form elements, but.. well there is another issue :
public static function valueCallback(&$element, $input, FormStateInterface $form_state) {
if ($input === FALSE) {
return NULL;
}
$parents = $element['#parents'];
$element_name = array_shift($parents); # <- problem here :/
$uploaded_files = \Drupal::request()->files->get('files', []);
$uploaded_file = $uploaded_files[$element_name] ?? NULL;
if ($uploaded_file) {
// Cast this to an array so that the structure is consistent regardless of
// whether #value is set or not.
return (array) $uploaded_file;
}
return NULL;
}
See how it doesn't care about whether or not the element has a #name explicitly defined ? and whether or not #parents is a tree ? Now because of those wrong assumptions on the element's name and its parents, you are somehow forced to either :
Leave the #name property unset and refer to the file later on validation/submit as 'params' (the parents root) instead of 'attachment'. Or,
Stick with #tree => FALSE. Or,
Provide your own #value_callback (deprecated ...?)
I am using Drupal 9. I want to give the ability to the admin to place a block and select from the block a taxonomy term in which will filter a content type.
I did the above by creating a custom block with the taxonomy "Sponsor Type" as you can see the screenshot bellow.
Also I use views_embed_view to pass the taxonomy as an argument and filter the data using Contextual filters
Custom block code:
<?php
namespace Drupal\aek\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a 'SponsorsBlock' block.
*
* #Block(
* id = "sponsors_block",
* admin_label = #Translation("Sponsors"),
* )
*/
class SponsorsBlock extends BlockBase {
/**
* {#inheritdoc}
*/
public function defaultConfiguration() {
return [
"max_items" => 5,
] + parent::defaultConfiguration();
}
public function blockForm($form, FormStateInterface $form_state) {
$sponsors = \Drupal::entityTypeManager()
->getStorage('taxonomy_term')
->loadTree("horigoi");
$sponsorsOptions = [];
foreach ($sponsors as $sponsor) {
$sponsorsOptions[$sponsor->tid] = $sponsor->name;
}
$form['sponsor_types'] = [
'#type' => 'checkboxes',
'#title' => $this->t('Sponsor Type'),
'#description' => $this->t('Select from which sponsor type you want to get'),
'#options' => $sponsorsOptions,
'#default_value' => $this->configuration['sponsor_types'],
'#weight' => '0',
];
$form['max_items'] = [
'#type' => 'number',
'#title' => $this->t('Max items to display'),
'#description' => $this->t('Max Items'),
'#default_value' => $this->configuration['max_items'],
'#weight' => '0',
];
return $form;
}
/**
* {#inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
$this->configuration['sponsor_types'] = $form_state->getValue('sponsor_types');
$this->configuration['max_items'] = $form_state->getValue('max_items');
}
/**
* {#inheritdoc}
*/
public function build() {
$selectedSponsorTypes = $this->configuration['sponsor_types'];
$cnxFilter = '';
foreach ($selectedSponsorTypes as $type) {
if ($type !== 0) {
$cnxFilter .= $type . ",";
}
}
return views_embed_view('embed_sponsors', 'default', $cnxFilter);
}
}
My issue now is how to limit the results. If you look above I have added an option "Max items to display" but using the Contextual filters I can't find any way to pass that argument to handle this.
If you use views_embed_view(), you will not be able to access the view object.
Load your view manually then you can set any properties before executing it:
use Drupal\views\Views;
public function build() {
$selectedSponsorTypes = $this->configuration['sponsor_types'];
$cnxFilter = '';
foreach ($selectedSponsorTypes as $type) {
if ($type !== 0) {
$cnxFilter .= $type . ",";
}
}
$view = Views::getView('embed_sponsors');
$display_id = 'default';
$view->setDisplay($display_id);
$view->setArguments([$cnxFilter]);
$view->setItemsPerPage($this->configuration['max_items']);
$view->execute();
return $view->buildRenderable($display_id);
}
My category url contains both id and slug like https://myapp.com/category/56-category-name (id field = 56 and slug field = category-name in the DB), when updating category name the slug field in DB is updated but the id still the same.
I would like to display my category whatever the slug provided that the id is correct and of course display the correct url. In this case SEO still correct i think.
Here my show method :
/**
* #Route("/category/{id}-{slug}", name="category_show", requirements={"id"="\d+"})
*/
public function show(CategoryRepository $categoryRepository, $slug, $id): Response
{
$category = $categoryRepository->find($id);
if($category->getSlug() !== $slug) {
return $this->redirectToRoute('category_show', [
'id' => $id,
'slug' => $category->getSlug()
]);
}
return $this->render('category/show.html.twig', [
'category' => $category
]);
}
It works if the given id exists in DB, othewise i got an error Call to a member function getSlug() on null. I can throw a NotFoundException, but i think this method use many times queries.
So please help me doing these properly with more flexibility and performance.
In case I would like to display the category in the post url as well like https://myapp.com/56-category-name/23-post-title how to go about it ??
Thanks in advance
Use findOneBy() and check if the result is null
/**
* #Route("/category/{id}-{slug}", name="category_show", requirements={"id"="\d+"})
* #Route("/{id}-{slug}/{idArticle}-{postSlug}", name="article_show", requirements={"idArticle"="\d+"})
*/
public function show(CategoryRepository $categoryRepository, $slug, $id, $idArticle = false, $postSlug = false ): Response
{
$category = $categoryRepository->findOneBy(['id'=>$id]);
if(is_null($category)){
throw new NotFoundHttpException(); //404, nothing found
}else{
//Category found.
if($idArticle){ // https://myapp.com/56-category-name/23-post-title
//Article route, put your logic here
}else{ //https://myapp.com/category/56-category-name
// Category route, logic here
if($category->getSlug() !== $slug) {
return $this->redirectToRoute('category_show', [
'id' => $id,
'slug' => $category->getSlug()
]);
}
return $this->render('category/show.html.twig', [
'category' => $category
]);
}
}
}
here is what i found good for my app :
in my CategoryController.php i create, the show method :
/**
* #Route("/{id}-{slug}", name="category_show", requirements={"id"="\d+"})
*/
public function show(CategoryRepository $categoryRepository, $slug = null, $id): Response
{
$category = $categoryRepository->find($id);
if (!$category) {
throw new NotFoundHttpException();
}
if($category->getSlug() !== $slug) {
return $this->redirectToRoute('category_show', [
'id' => $id,
'slug' => $category->getSlug()
]);
}
return $this->render('category/show.html.twig', [
'category' => $category
]);
}
in my index template to display the category_show link :
show
in my ArticleController.php i create, the show method :
/**
* #Route("/{category}/{id}-{slug}", name="article_show", requirements={"id"="\d+"})
*/
public function show(ArticleRepository $articleRepository, $slug = null, $id, $category = null): Response
{
$article = $articleRepository->find($id);
if (!$article) {
throw new NotFoundHttpException();
}
$cat = $article->getCategory()->getId() . '-' . $article->getCategory()->getSlug();
if($article->getSlug() !== $slug || $cat !== $category) {
return $this->redirectToRoute('article_show', [
'id' => $id,
'slug' => $article->getSlug(),
'category' => $cat
]);
}
return $this->render('article/show.html.twig', [
'article' => $article,
]);
}
in my index template to display the article_show link :
show
For me that solves my problem. But still, if we can improve the process, I'm a buyer.
Thanks
I read through a few posts about hook_form_alter() but couldn't make this work.
I want to create a custom module to override a menu $items 'title' for the Drupal core Tracker module.
function tracker_menu() {
// ....
$items['user/%user/track'] = array(
'title' => 'Track',
'page callback' => 'tracker_page',
'page arguments' => array(1, TRUE),
'access callback' => '_tracker_user_access',
'access arguments' => array(1),
'type' => MENU_LOCAL_TASK,
'file' => 'tracker.pages.inc',
);
// ...
}
I tried
function mymodule_tracker_menu_form_alter(&$form, &$form_state, $form_id) {
$items['user/%user/track']['title'] = 'Recent Content';
}
You are using the wrong hook. You have to use hook_menu_alter. hook_form_alter() is for forms.
/**
* Implements hook_menu_alter().
*/
function MYMODULE_menu_alter(&$items) {
$items['user/%user/track']['title callback'] = '_MYCALLBACK';
}
/**
* Custom title callback.
*/
function _MYCALLBACK() {
return t('Recent Content');
}
You could also use a preprocess function from your theme's template.php for that (actually much better, see template_process_page):
/**
* Implements template_process_page().
*/
function MYTEMPLATE_process_page(&$variables) {
if (arg(0) === 'user' && arg(2) === 'track) {
$variables['title'] = t('Recent Content');
}
}
Updated I fixed the preprocess_html hook as adviced, and added a pic of the structure of the module, maybe is something wrong there??
I just created a custom module for drupal-8 that ad a customizable block. Very simple, is currently working but now i want to add some look to the content of the block.
So my last attempt to achieve this was adding a libraries.yml to the module linking the block_header.css file and at the render array i added #prefix and #suffix with the css tags (div class='foo').
The code doesn't give me any error but it's not applying the font-weight of the css file.
Could you point me to the right direction?
This are the files:
block_header.libraries.yml
block_header:
version: 1.x
css:
theme:
css/block_header.css: {}
BlockHeader.php
<?php
namespace Drupal\block_header\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a 'Header' Block.
*
* #Block(
* id = "block_header",
* admin_label = #Translation("Block Header"),
* category = #Translation("Block Header"),
* )
*/
class BlockHeader extends BlockBase implements BlockPluginInterface {
function block_header_preprocess_html(&$variables) {
$variables['page']['#attached']['library'][] = 'Fussion_Modules/block_header/block_header';
}
/**
* {#inheritdoc}
*/
public function build() {
$config = $this->getConfiguration();
if (!empty($config['block_header_title']) && ($config['block_header_text'])) {
$title = $config['block_header_title'];
$descp = $config['block_header_text'];
}
else {
$title = $this->t('<div>Atención! Titulo no configurado!</div> </p>');
$descp = $this->t('<div>Atención! Descripción no configurada!</div>');
}
$block = array
(
'title' => array
(
'#prefix' => '<div class="title"><p>',
'#suffix' => '</p></div>',
'#markup' => t('#title', array('#title' => $title,)),
),
'description' => array
(
'#prefix' => '<div class="descp"><p>',
'#suffix' => '</p></div>',
'#markup' => t('#descp', array('#descp' => $descp,))
),
);
return $block;
}
/**
* {#inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$form = parent::blockForm($form, $form_state);
$config = $this->getConfiguration();
$form['block_header_title'] = array(
'#type' => 'textfield',
'#title' => $this->t('Titulo del Bloque'),
'#description' => $this->t('Titulo del Bloque'),
'#default_value' => isset($config['block_header_title']) ? $config['block_header_title'] : '',
);
$form['block_header_text'] = array(
'#type' => 'textarea',
'#title' => $this->t('Descripción'),
'#description' => $this->t('Descripción del bloque'),
'#default_value' => isset($config['block_header_text']) ? $config['block_header_text'] : '',
);
return $form;
}
/**
* {#inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
parent::blockSubmit($form, $form_state);
$values = $form_state->getValues();
$this->configuration['block_header_title'] = $values['block_header_title'];
$this->configuration['block_header_text'] = $values['block_header_text'];
$this->configuration['block_header_title'] = $form_state->getValue('block_header_title');
$this->configuration['block_header_text'] = $form_state->getValue('block_header_text');
}
}
block_header.css
.title{
font-weight: 500;
color:darkblue;
}
This is my module structure
Any ideas what i'm doing wrong?
Try updating your $block array that is being returned and remove the preprocess function:
$block = array
(
'title' => array
(
'#prefix' => '<div class="title"><p>',
'#suffix' => '</p></div>',
'#markup' => t('#title', array('#title' => $title,)),
),
'description' => array
(
'#prefix' => '<div class="descp"><p>',
'#suffix' => '</p></div>',
'#markup' => t('#descp', array('#descp' => $descp,))
),
'#attached' => array
(
'library' => array
(
'block_header/block_header'
)
)
);
An alternative following the advice on this page at drupal.org is to attach the css file in the build array of the block. So in block_header.libraries.yml you could write
block_header.tree:
css:
theme:
css/block_header.css: {}
And in BlockHeader.php
public function build() {
[...]
block = array (
'#attached' => array(
'library' => array(
'block_header/block_header.tree',
),
),
[...]
),
}
One way to do it is to:
Add libraries to block_header.info.yml file in your module:
libraries:
- block_header/block_header
Create block_header.libraries.yml file and add:
block_header:
version: 1.x
css:
module:
css/block_header.css: {}
Place css file you want to attach to css/block_header.css
Include css to custom form statement
Place the following line in form building part of the module:$form['#attached']['library'][] = 'block_header/block_header';
Rewrite following function in module file
function block_header_preprocess_html(&$variables) { $variables['page']['#attached']['library'][] = 'block_header/block_header'; }
So, i finally found the problem. The HOOK i was trying to implement to attach the *.css file it's need to be at the *.module file, wich i didn't had.
So i created the block_header.module with this HOOK:
<?php
/**
* Implements hook_preprocess_HOOK()
*/
function block_header_preprocess_block(&$variables) {
if ($variables['plugin_id'] == 'block_header') {
$variables['#attached']['library'][] = 'block_header/block_header';
}
}
After that i just deleted the HOOK i was using, so the final versión of the BlockHeader.php is:
<?php
namespace Drupal\block_header\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Block\BlockPluginInterface;
use Drupal\Core\Form\FormStateInterface;
/**
* Provides a 'Header' Block.
*
* #Block(
* id = "block_header",
* admin_label = #Translation("Block Header"),
* category = #Translation("Block Header"),
* )
*/
class BlockHeader extends BlockBase implements BlockPluginInterface {
/**
* {#inheritdoc}
*/
public function build() {
$config = $this->getConfiguration();
if (!empty($config['block_header_title']) && ($config['block_header_text'])) {
$title = $config['block_header_title'];
$descp = $config['block_header_text'];
}
else {
$title = $this->t('<div>Atención! Titulo no configurado!</div> </p>');
$descp = $this->t('<div>Atención! Descripción no configurada!</div>');
}
$block = array
(
'title' => array
(
'#prefix' => '<div class="title"><p>', /* HERE I ADD THE CSS TAGS */
'#suffix' => '</p></div>',
'#markup' => t('#title', array('#title' => $title,)),
),
'description' => array
(
'#prefix' => '<div class="descp"><p>', /* HERE I ADD THE CSS TAGS */
'#suffix' => '</p></div>',
'#markup' => t('#descp', array('#descp' => $descp,))
),
);
return $block;
}
/**
* {#inheritdoc}
*/
public function blockForm($form, FormStateInterface $form_state) {
$form = parent::blockForm($form, $form_state);
$config = $this->getConfiguration();
$form['block_header_title'] = array(
'#type' => 'textfield',
'#title' => $this->t('Titulo del Bloque'),
'#description' => $this->t('Titulo del Bloque'),
'#default_value' => isset($config['block_header_title']) ? $config['block_header_title'] : '',
);
$form['block_header_text'] = array(
'#type' => 'textarea',
'#title' => $this->t('Descripción'),
'#description' => $this->t('Descripción del bloque'),
'#default_value' => isset($config['block_header_text']) ? $config['block_header_text'] : '',
);
return $form;
}
/**
* {#inheritdoc}
*/
public function blockSubmit($form, FormStateInterface $form_state) {
parent::blockSubmit($form, $form_state);
$values = $form_state->getValues();
$this->configuration['block_header_title'] = $values['block_header_title'];
$this->configuration['block_header_text'] = $values['block_header_text'];
$this->configuration['block_header_title'] = $form_state->getValue('block_header_title');
$this->configuration['block_header_text'] = $form_state->getValue('block_header_text');
}
}
And that's it, now i'm getting the *.css file i created applied to the block created by the module.
Special thanks to #No Sssweat