How to show content of a custom block using PHP code format in Drupal 8 - drupal

I have added some custom code in a block using PHP code format to show that block on a specific page. I have checked all the things working fine on Devel PHP page but contents are not showing on page. The code below fetches the field value of a destination node.
$refer = $_SERVER[HTTP_REFERER];
$parsed = parse_url($refer);
$alias = array_pop($parsed);
$dst = \Drupal::service('path.alias_manager')->getPathByAlias($alias , $langcode);
$nid = array_pop(explode('/', $dst));
$dest_node = node_load($nid);
$body = $dest_node->get('body')->getValue();
print $body; //have tried other printing methods also but invain
Hope this clarifies the question.
Thanks

Are you sure that it works in Devel? I've just tried to execute your code, and this line:
$body = $dest_node->get('body')->getValue();
returns Array.
Try to use this one instead:
$body = $dest_node->body->value;

First of all, your first block of code (getting current node) can be replaced with just one line:
$node = \Drupal::service('current_route_match')->getParameter('node');
And the whole block can be changed in the following way:
if ($node = \Drupal::service('current_route_match')->getParameter('node')) {
print $node->body->value;
}
P.S. And it's definitely a bad idea to use PHP text filter. You may easily write your own custom module providing required block. The simplest block plugin requires several lines of code:
/**
* #file
* Contains \Drupal\my_module\Plugin\Block\MyBlock.
*/
namespace Drupal\my_module\Plugin\Block;
use Drupal\Core\Block\BlockBase;
/**
* Provides my super block.
*
* #Block(
* id = "my_module_block",
* admin_label = #Translation("My Block"),
* category = #Translation("My Module"),
* )
*/
class MyBlock extends BlockBase{
/**
* Builds and returns the renderable array for this block plugin.
*
* #return array
* A renderable array representing the content of the block.
*
* #see \Drupal\block\BlockViewBuilder
*/
public function build() {
if ($node = \Drupal::service('current_route_match')->getParameter('node')) {
return [ '#markup' => $node->body->value ];
}
}
}
This file MyBlock.php must be placed in /src/Plugin/Block/ directory inside your custom module named my_module.

Related

Dynamic number of Contact Form 7 recipients

I am working a on a form with, at it's core, two fieldset: main and "other recipient"; at the end of of the "other recipient" fieldset, I have a "add another recipient" link.
Here's who needs what:
Main recipient: everything
Other recipient: the "other recipient" fieldset;
Sub-sequent recipients: Respective fieldsets
So far, I've been looking at the Documentation but not much luck there, not that I expected any, either.
Edit
I think this is unclear, so I will be a little more explicit as to what is the context. My form is a registration where we can sign up multiple people; one of the fields is labeled "Your email". Since we can register more than one person at once, I need to duplicate the fieldset containing "Your email".
Edit 2
To help clarify, imagine that we are signing up kids for a summer camp. The first fieldset is general, say the parent's billing information, and the second fieldset is the child's information. The parent needs to be able to fill out a single form and dynamically add as many children as the parent desires.
In each of the children's fieldset, their email is required and they receive the information relevant to this child, where the email would be similar to:
Hello {children's name},
You've been registered to StackOverflow Summer Camp. Here's the information you need to know:
[...]
Thanks for being a good sport!
Hope this helps.
When you've got a specific use case like this, shoehorning the functionality into peripherally related plugins often results in frustration. That being said - there are times where you're married to a specific plugin or approach, and just have to build on top of it.
With that caveat out of the way, I think you should approach this from the angle of creating a new fieldtype for Contact Form 7. This way you have control over rendering the field's HTML, the data validation, among other things. It might also provide an launch point for DB storage and sending reminders, later, as you've mentioned in a comment on another answer.
Here's this approach in action:
The new fieldtype is called emailplus, and you include it into a form like this:
<div class="cf7-duplicable-recipients">
<label>Main Recipient (required)</label>
[emailplus emails]
[submit "Sign Up"]
</div>
Additionally, I've set the recipient under the "mail" panel in the form's settings to [emails].
If an emailsplus field is set as the recipient, the class overrides the default wpcf7 behaviour and sends mail to each value in the email array, substituting any [emails] placeholders in the body of the message on a per-email basis.
The emailplus fieldtype is encapsulated here in a class, and commented liberally:
<?php
class WPCF7_Duplicable_Email
{
/**
* The emails this form's field is addressed to
*
* #var array
*/
public $emails = array();
/**
* The name of the tag bearing the emailplus type
*
* #var string
*/
public $tag_name;
/**
* Instantiate the class & glom onto the wpcf7 hooks
*
* #return void
*/
public function __construct()
{
add_action('wpcf7_init', array($this, 'add_emailplus_tag'));
add_action('wpcf7_form_tag', array($this, 'assign_values_to_field'));
add_filter('wpcf7_validate_emailplus', array($this, 'validate'), 2);
add_action('wpcf7_before_send_mail', array($this, 'send_email'));
add_action('wp_enqueue_scripts', array($this, 'emailplus_scripts'));
}
/**
* Add our the [emailplus __YOUR_FIELD_NAME__] shortcode for use in wpcf7 forms.
*
* #uses wpcf7_add_shortcode
*
* #return void
*/
public function add_emailplus_tag()
{
wpcf7_add_shortcode(
array('emailplus'),
array($this, 'shortcode_handler'),
true
);
}
/**
* Renders the HTML for the [emailplus] shortcode
*
* Referenced from wpcf7_text_shortcode_handler in wp-content/plugins/contact-form-7/modules/text.php
*
* #param array $tag The data relating to the emailplus form field.
*
* #uses wpcf7_get_validation_error
* #uses wpcf7_form_controls_class
*
* #return string
*/
public function shortcode_handler(array $tag) {
$tag = new WPCF7_Shortcode($tag);
if (true === empty($tag->name)) {
return '';
}
$validation_error = wpcf7_get_validation_error($tag->name);
$class = wpcf7_form_controls_class($tag->type);
if ($validation_error) {
$class .= ' wpcf7-not-valid';
}
$atts = array(
'class' => $tag->get_class_option($class),
'id' => $tag->get_id_option(),
'tabindex' => $tag->get_option('tabindex', 'int', true),
'aria-invalid' => $validation_error ? 'true' : 'false',
'type' => 'email',
'name' => $tag->name.'[]', // <-- Important! the trailing [] Tells PHP this is an array of values
'value' => $tag->get_default_option()
);
$atts = wpcf7_format_atts($atts);
$html = sprintf(
'<div class="emailplus-wrapper %1$s"><input %2$s />%3$s</div>',
sanitize_html_class($tag->name),
$atts,
$validation_error
);
// We identify the container that will hold cloned fields with the [data-wpcf7-duplicable-email] attr
return '<div class="wpcf7-form-control-wrap %1$s" data-wpcf7-duplicable-email>'.$html.'</div>';
}
/**
* Validates the value of the emailplus tag.
*
* Must be handled separately from other text-based form inputs,
* since the submitted POST value is an array.
*
* We can safely assume emailplus while creating the WPCF7_Shortcode,
* because we know we hooked this function onto 'wpcf7_validate_emailplus'
*
* #uses wpcf7_is_email
* #uses WPCF7_Validation::invalidate
*
* #param WPCF7_Validation $result The validation helper class from wpcf7.
* #param array $tag The array of values making up our emailplus tag
*
*/
public function validate(WPCF7_Validation $result, $tag)
{
$tag = new WPCF7_Shortcode(
array(
'basename' => 'emailplus',
'name' => $this->tag_name,
'raw_values' => $this->emails
)
);
// Check each value of the form field.
// Emails must be validated individually.
foreach($tag->raw_values as $email) {
if (false === wpcf7_is_email($email)) {
$result->invalidate($tag, wpcf7_get_message('invalid_email'));
}
}
return $result;
}
/**
* For completeness' sake, manually assign the value to the emailplus fieldtype.
*
* Wpcf7 doesn't know how to treat our fieldtype's value by default.
*
* As a side effect, this sets the email addresses that are up for delivery.
*
* #param array $scanned_tag The tag that wpcf7 is scanning through, and processing.
*
* #return $array;
*/
public function assign_values_to_field($scanned_tag)
{
if ($scanned_tag['type'] !== 'emailplus') {
return $scanned_tag;
}
$this->tag_name = $scanned_tag['name'];
if (key_exists($scanned_tag['name'], $_POST)) {
// Stores the emails on a class property for use later.
$this->emails = $_POST[$scanned_tag['name']];
$scanned_tag['raw_values'] = $this->emails;
$scanned_tag['values'] = $this->emails;
}
return $scanned_tag;
}
/**
* Performs a substitution on the emailplus field's fieldname, on a per-value basis.
*
* Ex. in two parts
* 1 - The shortcode [emailsplus emails] renders into <input type="email" name="emails[]" value="" >
* Which the user clones and submits, processing into something like
* ['test1#gmail.com', 'test2#gmail.com'].
* 2 - The user has set [emails] as the recipient in the "mail" panel for the form.
*
* Because wpcf7 isn't aware of how to process a field w/ multiple values when emailing,
* we loop over the values of [emails], replace the tag, and invoke WPCF7_Mail::send()
* for each value.
*
* #param WPCF7_ContactForm $form The contact form object.
*
* #uses WPCF7_Mail::send
*
* #return void
*/
public function send_email(WPCF7_ContactForm $form)
{
$placeholder = '['.$this->tag_name.']';
if (false === strpos($form->prop('mail'), $placeholder)) {
return;
}
foreach ($this->emails as $email) {
$template = $form->prop('mail');
$template['recipient'] = str_replace($placeholder, $email, $template['recipient']);
$template['body'] = str_replace($placeholder, $email, $template['body']);
WPCF7_Mail::send($template);
}
// Tell cf7 to skip over the default sending behaviour in WPCF7_Submission->mail()
$form->skip_mail = true;
}
/**
* Adds our js that will clone the emailplus field.
*
* Could be optimized with a conditional that checks if there is a form with the [emailplus]
* fieldtype somewhere on the page
*
* #return void
*/
public function emailplus_scripts()
{
wp_enqueue_script(
'cf7-duplication',
get_template_directory_uri() . '/js/cf7-duplication.js',
array('jquery'),
'20161006',
true
);
}
}
$wpcf7DuplicableEmail = new WPCF7_Duplicable_Email();
And, the .js file that handles the cloning. It should live in /path/to/your/theme/js/cf7-duplication.js'.
(function($) {
$(document).ready(function() {
var addEmailField = function(inputRow, container) {
inputRow.find('input[type=email]').val('');
var removeButton = $('×')
.click(function(e) {
e.preventDefault();
inputRow.remove();
});
inputRow.append(removeButton);
inputRow.insertAfter(container.find('.emailplus-wrapper').last());
}
$.each($('[data-wpcf7-duplicable-email]'), function(i, el) {
var container = $(el);
var inputRow = container.find('.emailplus-wrapper');
var addButton = $('Add another email');
addButton.click(function(e) {
e.preventDefault();
var newEmailField = inputRow.clone();
addEmailField(newEmailField, container);
});
container.append(addButton);
});
});
})(jQuery);
Last, but not least, if you'd like the form to fadeout when it's valid and the emails have gone out, add this to the "additional settings" panel.
on_sent_ok: "jQuery('.cf7-duplicable-recipients').fadeOut();"
This approach is best for CF7's AJAX submissions. If you want to extend it to handle vanilla POST requests, you could update the shortcode handler to render multiple <input> fields where you need to preserve the value attr on invalid submissions.
Options
1) Under the Mail tap in the setup menu, after you click Mail(2), in the TO: field add this line. Should the parent have less than the number of kids stated, the extra email addresses should do nothing. [email],[email] is the basic format.
[parents-email], [kid-email1], [kid-email2], [kid-email3], [kid-email4], [kid-email5], [kid-email6], [kid-email7]
Option 2) In the TO: field, just place 1 email address such as the parents. and in the Additional Headers: place the code below
CC: [kid-email1], [kid-email2], [kid-email3], [kid-email4], [kid-email5], [kid-email6], [kid-email7]
or
BCC: [kid-email1], [kid-email2], [kid-email3], [kid-email4], [kid-email5], [kid-email6], [kid-email7]
A possible problem that could arise: Many hosts block things like this to prevent spam. If theses do not work, then this is probably the case. You would need to contact your hosting provider about removing the block.
Contact form 7 has only one recipient field, however you can enter multiple email address with comma separated in that field, example "email1#domain.com,email2#domain.com,email3#domain.com".
So for your case, use JavaScript to add multiple duplicate recipient fields dynamically, and finally on form submit write a JavaScript function to concat all the recipient email addresses then keep that in main recipient field and submit the form. Hope you got my point.

Drupal - Print Database Output to custom block

I created a little module with the DB fields "tag", "tag_url" and "tag_weight" and a form to fill them up.
Now I want to output the DB values in a custom block, but I dont know how to create it the correct way :-/
/**
* Implements hook_block_info().
*
* This hook declares what blocks are provided by the module.
*/
function myModule_block_info() {
$blocks['example_uppercase'] = array(
// info: The name of the block.
'info' => t('Example: uppercase this please'),
'status' => TRUE,
);
return $blocks;
}
/**
* Implements hook_block_view().
*
* This hook generates the contents of the blocks themselves.
*/
function myModule_block_view($delta = '') {
//The $delta parameter tells us which block is being requested.
switch ($delta) {
case 'example_uppercase':
//Select items from DB
$result = db_select('myModule','ht')
->fields('ht',array('tag','tag_url','tag_weight'))
->execute();
**$block['content'] = foreach($result as $value) { echo $value->tag; }**
break;
}
return $block;
}
When I am trying to do this, I get:
PHP Parse error: syntax error, unexpected T_FOREACH in
/.module on line 49
How to print out the database values to the block?
I figured out that i need to do it that way:
https://drupal.org/node/1104498

How to pass multiple variables from twig to symfony controller action with render

I am attempting to render an action from within twig to list results of a table. The first variable is passing through fine and I am getting the list populated, but the second variable is not arriving.
{% render 'RiskAssessmentBundle:Assessment:assessmentlist' with {'offenderId': entity.getId, 'assessmentStatus': 'Complete' } %}
Above is the render call in my twig template. offenderId is arriving with a value.
/**
* List widget
*
* #param int $offenderId id of the offender
* #param string $assessmentStatus const value of desired status
*
* #Template("RiskAssessmentBundle:Assessment:assessmentlist.html.twig")
*/
public function assessmentlistAction($offenderId, $assessmentStatus = null)
{
var_dump($assessmentStatus);
$em = $this->getDoctrine()->getEntityManager();
$offender = $em->getRepository('RiskOffenderBundle:Offender')->find($offenderId);
if (is_null($assessmentStatus)) {
$assessments = $em->getRepository('RiskAssessmentBundle:Assessment')
->findAllByActive(true, $offenderId, 5);
} else {
$assessments = $em->getRepository('RiskAssessmentBundle:Assessment')
->findAllByStatus($assessmentStatus, $offenderId, 5);
}
return array(
'assessments' => $assessments,
'offender' => $offender,
);
}
Above is the action called. The var_dump of the $assessmentStatus is always NULL. I am new to symfony, but every piece of documentation I can find makes me think this should work. Anyone have a clue what I am doing wrong?
The above code works perfectly. We have an extension system running and the entire twig template had been extended. The render I modified was not the render being called. Once I modified the other template it all works perfectly.

Is there a way (other than sql) to get the mlid for a given nid in drupal?

I've got a node, I want it's menu. As far as I can tell, node_load doesn't include it. Obviously, it's trivial to write a query to find it based on the path node/nid, but is there a Drupal Way to do it?
if the menu tree has multiple levels sql seems a better option.
a sample for drupal 7 is given bellow where path is something like 'node/x'
function _get_mlid($path, $menu_name) {
$mlid = db_select('menu_links' , 'ml')
->condition('ml.link_path' , $path)
->condition('ml.menu_name',$menu_name)
->fields('ml' , array('mlid'))
->execute()
->fetchField();
return $mlid;
}
The Menu Node module exposes an API to do this.
You can read the documentation (Doxygen) in the code. I think the functionality you need is provided by the menu_node_get_links($nid, $router = FALSE) method:
/**
* Get the relevant menu links for a node.
* #param $nid
* The node id.
* #param $router
* Boolean flag indicating whether to attach the menu router item to the $item object.
* If set to TRUE, the router will be set as $item->menu_router.
* #return
* An array of complete menu_link objects or an empy array on failure.
*/
An associative array of mlid => menu object is returned. You probably only need the first one so it might look like something like this:
$arr = menu_node_get_links(123);
list($mlid) = array_keys($arr);
Otherwise, you can try out the suggestion in a thread in the Drupal Forums:
Use node/[nid] as the $path argument for:
function _get_mlid($path) {
$mlid = null;
$tree = menu_tree_all_data('primary-links');
foreach($tree as $item) {
if ($item['link']['link_path'] == $path) {
$mlid = $item['link']['mlid'];
break;
}
}
return $mlid;
}

OR operator in Drupal View Filters

I need to implement an OR operator between some filters in a Drupal View.
By default, Drupal AND's every filter together.
By using
hook_views_query_alter(&$view, &$query)
I can access the query ( var $query ) , and I can change either :
$query->where[0]['type']
to 'OR', or
$query->group_operator
to 'OR'
The problem is however, that I do not need OR's everywhere. I've tried changing both of them to OR seperately, and it doesn't yield the desired result.
It seems changing those values, puts OR's everywhere, while I need => ( filter 1 AND filter 2 ) OR ( filter 3 ), so just 1 OR.
I could just check the Query of the View, copy it, modify it, and run it through db_query, but that's just dirty ..
Any suggestions ?
Thx in advance.
If you are using Views 3 / Drupal 7 and looking for the answer to this question, it is baked right into Views. Where it says "add" next to filters, click the dropdown, then click "and/or; rearrange". It should be obvious from there.
Unfortunately this is still a missing feature in Views2. It has long been asked for and was promised a while ago, but seems to be a tricky piece of work and is now scheduled for Views3.
In the meantime you could try the Views Or module mentioned in that thread. As of today, it is still in dev status, but seems to be actively maintained and the issue queue does not look to bad, so you might want to give it a try.
if you want do it with view_query_alter hook, you should use $query->add_where() where you can specify if it's AND or OR. From views/include/query.inc
/**
* Add a simple WHERE clause to the query. The caller is responsible for
* ensuring that all fields are fully qualified (TABLE.FIELD) and that
* the table already exists in the query.
*
* #param $group
* The WHERE group to add these to; groups are used to create AND/OR
* sections. Groups cannot be nested. Use 0 as the default group.
* If the group does not yet exist it will be created as an AND group.
* #param $clause
* The actual clause to add. When adding a where clause it is important
* that all tables are addressed by the alias provided by add_table or
* ensure_table and that all fields are addressed by their alias wehn
* possible. Please use %d and %s for arguments.
* #param ...
* A number of arguments as used in db_query(). May be many args or one
* array full of args.
*/
function add_where($group, $clause)
I added it by concatenating the string.
It is relatively specific to the implementation - people would need to play with field to match for OR - node.title in the following code and the field to match it with - node_revisions.body in this case.
Extra piece of code to make sure that node_revisions.body is in the query.
/**
* Implementation of hook_views_api().
*/
function eventsor_views_api() { // your module name into hook_views_api
return array(
'api' => 2,
// might not need the line below, but in any case, the last arg is the name of your module
'path' => drupal_get_path('module', 'eventsor'),
);
}
/**
*
* #param string $form
* #param type $form_state
* #param type $form_id
*/
function eventsor_views_query_alter(&$view, &$query) {
switch ($view->name) {
case 'Events':
_eventsor_composite_filter($query);
break;
}
}
/**
* Add to the where clause.
* #param type $query
*/
function _eventsor_composite_filter(&$query) {
// If we see "UPPER(node.title) LIKE UPPER('%%%s%%')" - then add and to it.
if (isset($query->where)) {
$where_count = 0;
foreach ($query->where as $where) {
$clause_count = 0;
if (isset($where['clauses'])) {
foreach ($where['clauses'] as $clause) {
$search_where_clause = "UPPER(node.title) LIKE UPPER('%%%s%%')";
// node_data_field_long_description.field_long_description_value
$desirable_where_clause = "UPPER(CONCAT_WS(' ', node.title, node_revisions.body)) LIKE UPPER('%%%s%%')";
if ($clause == $search_where_clause) {
// $query->add_where('or', 'revisions.body = %s'); - outside of what we are looking for
$query->where[$where_count]['clauses'][$clause_count] = $desirable_where_clause;
// Add the field to the view, just in case.
if (!isset($query->fields['node_revisions_body'])) {
$query->fields['node_revisions_body'] = array(
'field' => 'body',
'table' => 'node_revisions',
'alias' => 'node_revisions_body'
);
}
}
$clause_count++;
}
}
$where_count++;
}
}
}

Resources