Override page title for Tracker module pages in Drupal - drupal

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');
}
}

Related

After submitting the form, providing a file, I get the error: "Field is required"

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 ...?)

upload a file in a drupal 8 contributed module

I am trying to create my first drupal 8 module. in this module I have to create a new form and provide user a file uploading capability in this form. here is my form controller:
class Make2d extends FormBase {
/**
* {#inheritdoc}
*/
public function getFormId() {
return 'make2d_form';
}
/**
* {#inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
if (\Drupal::currentUser()->isAnonymous()) {
return new RedirectResponse(\Drupal::url('user.page'));
}
$form['sheet_size'] = array(
'#type' => 'radios',
'#title' => t('Sheet Size'),
'#options' => array(t('10 X 10(2.99$)'), t('17 X 17(4.99$)'), t('28 X 28(5.99$)')),
);
$form['uploaded_file'] = array(
'#type' => 'file',
'#title' => t('Upload your file'),
'#required' => true
);
$form['actions']['#type'] = 'actions';
$form['actions']['submit'] = array(
'#type' => 'submit',
'#value' => $this->t('Save to Cart'),
'#button_type' => 'primary',
);
return $form;
}
/**
* {#inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
echo '<pre>';
print_r($form_state->getvalues());
echo '</pre>';
}
/**
* {#inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
print_r($form_state['values']);
}
}
this is the result when I go to my form's page:
image of my form
then I choose a file from my computer and submit the form. but when I print_r my $form_state->getvalues() array the result is sth like this:
Array
(
[sheet_size] => 0
[uploaded_file] =>
[submit] => Drupal\Core\StringTranslation\TranslatableMarkup Object
...
you can see that [uploaded_file] is empty. and there is an error on top of the form about uploading a file. what is wrong with the form controller and file uploading.
thanks.
at last! I find it. we should use '#type' = 'managed_file' so that we let drupal to manage uploaded file. in the case we use '#type' = 'file' we must transfer file ourselves by file_save_upload().

How to add a custom callback method in configureListFields() sonata admin?

I want to add a custom callback method on a sonata field list configureListFields()
It's possible in the configureDatagridFilters() method like below:
protected function configureDatagridFilters(DatagridMapper $filter)
{
$filter
->add('user', 'doctrine_orm_callback', array(
'callback' => array($this, 'customMethod'),
));
}
But is it possible in the configureListFields() method ?
Your attempt using the doctrine_orm_callback is correct:
Doctrine2 ORM Admin's documentation - Reference - Filter Field Definition (2.2) - 5.5.3. Callback
Use the provided example and replace comments with your bookings and then apply your conditions to the builder:
protected function configureDatagridFilters(DatagridMapper $datagridMapper)
{
$datagridMapper
->add('title')
->add('enabled')
->add('tags', null, array(), null, array('expanded' => true, 'multiple' => true))
->add('author')
->add('with_open_comments', 'doctrine_orm_callback', array(
'callback' => function($queryBuilder, $alias, $field, $value) {
if (!$value) {
return;
}
$queryBuilder->leftJoin(sprintf('%s.comments', $alias), 'c');
$queryBuilder->andWhere('c.status = :status');
$queryBuilder->setParameter('status', Comment::STATUS_MODERATE);
return true;
},
'field_type' => 'checkbox'
))
;
}

Drupal page callback in menu_alter. How to use callback before redefining

In module User:
$items['user/%user/edit'] = array(
'title' => 'Edit',
'page callback' => 'drupal_get_form',
'page arguments' => array('user_profile_form', 1),
'access callback' => 'user_edit_access',
'access arguments' => array(1),
'type' => MENU_LOCAL_TASK,
'file' => 'user.pages.inc',
);
In my module:
function MYMODULE_menu_alter(&$items) {
$items['user/%user/edit']['access callback'] = 'MYMODULE_can_edit_user';
$items['user/%user/edit']['access arguments'] = array(1);
$items['user/%user/edit']['page callback'] = 'MYMODULE_user_edit_page';
}
function MYMODULE_user_edit_page($account){
if(my_condition){go to page... }
else { use callback from module User }
}
How to use default page callback from module User if my_condition in function *MYMODULE_user_edit_page* is not TRUE ?
What about...
function MYMODULE_menu_alter(&$items) {
$items['user/%user/edit']['access callback'] = 'MYMODULE_can_edit_user';
$items['user/%user/edit']['access arguments'] = array(1);
$current_callback = $items['user/%user/edit']['page callback'];
$items['user/%user/edit']['page callback'] = 'MYMODULE_user_edit_page';
$items['user/%user/edit']['page arguments'][] = $current_callback;
}
function MYMODULE_user_edit_page($existing_callback = NULL){
if(my_condition){go to page... }
elseif (!empty($existing_callback)) {
$existing_callback($account);
}
}

Drupal: I need to display the user name in a block

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)))),
);
}
}

Resources