Drupal 7 tokens and img tags - drupal-7

I am using Drupal 7, webform and webform2pdf modules to create an application form, that once filled out, will generate a PDF with all of the information from the applicant.
Problem that I am having is that the fields I set up for uploading a .jpg head shot and a .pdf of a short explanation are showing as links instead of just listing the content.
I am using tokens generated from the webform components to dynamically fill the content into the PDF.
for example [submission:value:recent_photo]
is there a special syntax I need to display the image instead of just the link?

Here's the solution i've found to solve the same problem (Drupal 7, Webform 4).
Type this function in MYADMINTHEME/template.php
function MYADMINTHEME_webform_display_file($variables) {
$element = $variables['element'];
$current_path = current_path();
$file = $element['#value'];
$filemime = !empty($file) ? $element['#value']->filemime : '';
if (substr_count($current_path, 'downloadpdf') && in_array($filemime, array('image/jpeg','image/gif','image/png'))){
if ($element['#format'] == 'text')
return !empty($file) ? webform_file_url($file->uri) : t('no upload');
$url = drupal_realpath($file->uri);
return '<img src="' . $url . '"/>';
} else {
$url = !empty($file) ? webform_file_url($file->uri) : t('no upload');
return !empty($file) ? ($element['#format'] == 'text' ? $url : l($file->filename, $url)) : ' ';
}
}

Related

Drupal Custom Module HTML

So I've only just begun to learn Drupal so If you believe I'm going about this the wrong way, please let me know.
I have a content type called Events. I'm basically just trying to output a snippet of the latest event on the home page. To do this, I've created a custom module following the Drupal tutorial Drupal doc's custom module tutorial
Here's my module's code
<?php
/**
* Implements hook_block_info().
*/
function latest_event_block_info() {
$blocks['latest_event'] = array(
// The name that will appear in the block list.
'info' => t('Latest Event'),
// Default setting.
'cache' => DRUPAL_CACHE_PER_ROLE,
);
return $blocks;
}
/**
* Custom content function.
*
* Set beginning and end dates, retrieve posts from database
* saved in that time period.
*
* #return
* A result set of the targeted posts.
*/
function latest_event_contents(){
//Get today's date.
$today = getdate();
//Calculate the date a week ago.
$start_time = mktime(0, 0, 0,$today['mon'],($today['mday'] - 7), $today['year']);
//Get all posts from one week ago to the present.
$end_time = time();
//Use Database API to retrieve current posts.
$query = new EntityFieldQuery;
$query->entityCondition('entity_type', 'node')
->entityCondition('bundle', 'event')
->propertyCondition('status', 1) // published == true
->propertyCondition('created', array($start_time, $end_time), 'BETWEEN')
->propertyOrderBy('created', 'DESC') //Most recent first.
->range(0, 1); //ony grab one item
return $query->execute();
}
/**
* Implements hook_block_view().
*
* Prepares the contents of the block.
*/
function latest_event_block_view($delta = '') {
switch ($delta) {
case 'latest_event':
$block['subject'] = t('Latest Event');
if (user_access('access content')) {
// Use our custom function to retrieve data.
$result = latest_event_contents();
$nodes = array();
if (!empty($result['node'])) {
$nodes = node_load_multiple(array_keys($result['node']));
}
// Iterate over the resultset and generate html.
foreach ($nodes as $node) {
//var_dump($node->field_date);
$items[] = array(
'data' => '<p>
<span class="text-color">Next Event</span> ' .
$node->field_date['und'][0]['value'] . ' ' .
'</p>' .
'<p>' .
$node->title . ' ' .
$node->field_location['und'][0]['value'] . ' ' .
'</p>'
);
}
// No content in the last week.
if (empty($nodes)) {
$block['content'] = t('No events available.');
}
else {
// Pass data through theme function.
$block['content'] = theme('item_list', array(
'items' => $items));
}
}
return $block;
}
}
I've added the block to a region, and it renders fine in my template page. However, the events are outputted in a list which is not what I want.
Here's how the block is being rendered in HTML
<div class="item-list">
<ul>
<li class="first last">
<p>
<span class="text-color">Next Event</span> June 23 2016 18:30 - 21:00 </p><p>Cancer Research UK Angel Building, 407 St John Street, London EC1V 4AD
</p>
</li>
</ul>
</div>
So assuming I'm going about this whole thing correctly, how can I modify this blocks html? Thanks!
I think first you need to understand the theme('item_list', ....). This always output a HTML list either UL or OL as given.
If you want to show your content without the HTML list wrapper, you could try this:
/**
* Implements hook_block_view().
*
* Prepares the contents of the block.
*/
function latest_event_block_view($delta = '') {
switch ($delta) {
case 'latest_event':
$block['subject'] = t('Latest Event');
if (user_access('access content')) {
// Use our custom function to retrieve data.
$result = latest_event_contents();
$nodes = array();
if (!empty($result['node'])) {
$nodes = node_load_multiple(array_keys($result['node']));
}
// Iterate over the resultset and generate html.
$output = '';
foreach ($nodes as $node) {
//var_dump($node->field_date);
$output .= '<p>
<span class="text-color">Next Event</span> ' .
$node->field_date['und'][0]['value'] . ' ' .
'</p>' .
'<p>' .
$node->title . ' ' .
$node->field_location['und'][0]['value'] . ' ' .
'</p>';
}
// No content in the last week.
if (empty($output)) {
$block['content'] = t('No events available.');
}
else {
// Pass data through theme function.
$block['content'] = $output;
}
}
return $block;
}
}
That is one way. Another way is to use your own custom them template and use the array to output through that. For eg.
// Pass data to template through theme function.
$block['content'] = theme('latest_event_block_template', $items);
Then define a hook_theme function to get this to a template, like:
function latest_event_theme() {
return array(
'latest_event_block_template' => array(
'arguments' => array('items' => NULL),
'template' => 'latest-event-block-template',
),
);
}
Now, you should have a template at the module's root directory with the name latest-event-block-template.tpl.php. On this template you will be able to get the $items array and adjust the HTML yourself. Don't forget to clear theme registry after creating the template.
Hope it helps!
It's outputting as a list because you are passing $block['content'] the item_list theme function.
What you could do instead is create your own custom theme template using hook_theme. This will let you use custom markup in a custom template file.
After, replace this:
// Pass data through theme function.
$block['content'] = theme('item_list', array('items' => $items));
With this:
// Pass data through theme function.
$block['content'] = theme('my_custom_theme', array('items' => $items));

Find all in CakePHP is dumping a semicolon in my view

I don't know what exactly is happening with my CakePHP app. It worked last week and I have literally changed nothing with that particular file.
When I use find 'all' in my controller it dumps a semi-colon in my view even if there is nothing in the view file.
Here is my code
$evts = $this->Event->find('all');
There is nothing in my view file. I don't know if it makes a difference but I'm using a json view.
As requested here is the complete code for the action
public function search(){
$this->Event->recursive = 2;
$conditions = array();
if(!empty($this->request->query['name'])){
$conditions = array('Event.name LIKE ' => "%" . str_replace(" ","%", $this->request->query['name']) . "%");
}
if(!empty($this->request->query['home'])){
$conditions = array('Event.home_team_id' => $this->request->query['home']);
}
if(!empty($this->request->query['away'])){
$conditions = array('Event.away_team_id' => $this->request->query['away']);
}
$limit = 25;
if(!empty($this->request->query['limit'])){
$limit = $this->request->query['limit'];
}
//$evts = $this->Event->find('all',array('conditions'=>array($conditions),'order' => array('Event.start_time'),'limit'=>$limit));
$evts = $this->Event->find('all');
$this->set('events',$evts);
}
Everything in the view has been commented out ... but here is the code anyway
$results = array();
$i = 0;
foreach ($events as $event) {
$results[$i]['id'] = $event['Event']['id'];
$results[$i]['label'] = $event['Event']['name'] . "(" . date_format(date_create($event['Event']['start_time']), 'D, jS M Y') . ")";
$results[$i]['value'] = $event['Event']['name'];
$results[$i]['home_team_name'] = $event['HomeTeam']['name'];
$results[$i]['away_team_name'] = $event['AwayTeam']['name'];
$results[$i]['sport_name'] = $event['Sport']['name'];
$results[$i]['tournament_name'] = $event['Tournament']['name'];
$results[$i]['start_time'] = $event['Event']['start_time'];
$results[$i]['img'] = $event['Event']['img_path'];
$results[$i]['listener_count'] = 0; //TODO Get the follower count
$i++;
}
echo json_encode($results);
Display
If you changed nothing with that particular file then perhaps you have installed callbacks (either in the controller or in the model) that echo that ";". Look for 'afterFind' calls...
Search your model and your controller folders for "echo statement". In fact you should search your entire app folder except for the Views folder.

using same array in different functions

Here's the situation: I have 2 different functions and one view. I need to send data from function 1 to the view (2 arrays), and from that view i need to send data to function 2 (1 array).
Sending data from function 1 to the view is an easy job, but i don't know how to do it with function 2 because the information i would like to send to it has a function 1's array plus other new data the users entry.
I know there's no chance to send an array through URL, but i'm out of ideas.
Which is the best option for passing the data?
i send to the view this data:
admin: (part of the function 1)
//last part of the code
$this->data['conditions'] = $conditions;
$this->data['notselectedconditions'] = $notselectedconditions;
$this->parser->parse('admin/tools/showReport.tpl',$this->data);
In the view i use the information on those arrays, and user can put some new entry.
view:
<dt>Tipo de Usuario<dt>
<dd>Pre Registrado</dd>
reloadConditions is function 2, the one that needs what function 1 provides plus the selection of the user in order to keep filtering the results.
The information i need to have available on function 2 is: array 'conditions' and users new entry
After a long discussion and some clarifications in chat, here is the solution I came up with:
<?php
function addQueryParameters($url, $params) {
$paramsStr = http_build_query($params);
$fragment = parse_url($url, PHP_URL_FRAGMENT);
$query = parse_url($url, PHP_URL_QUERY);
$interUrl = '';
if ($query === NULL) {
$interUrl = '?';
}
if ($fragment !== NULL) {
$interUrl = '&';
}
$interUrl .= $paramsStr;
if ($fragment !== NULL) {
$pos = strpos($url, '#' . $fragment);
$url = substr($url, 0, $pos) . $interUrl . substr($url, $pos);
}
return $url;
}
function linkifyFilterConditions($url, $conditions) {
return addQueryParameters($url, array('conditions' => $conditions));
}
// Tests
// TODO: replace by unit tests
var_dump(linkifyFilterConditions('http://example.com/blub', [1,2,3]));
var_dump(linkifyFilterConditions('http://example.com/blub#a', [1,2,3]));
var_dump(linkifyFilterConditions('http://example.com/blub?a=b#a', [1,2,3]));
Usage (in OP's views):
$href = 'yourscript?conditions[]=a';
$href = linkifyFilterConditions($href, $conditions);
echo '<a href="' . $href . '">Test</>';
How about puting you arrays in $_SESSION?
see here http://www.phpriot.com/articles/intro-php-sessions/7

how to set the full base url to a cdn while using HtmlHelper in cakephp?

My cakephp app is running on 2.4.0
It is already running live on yourapp.com
I am attempting to use Amazon CloudFront to serve static assets like css, js, and images.
The CDN domain I chose was cdn.yourapp.com
Sadly, when I tried to use it this way:
echo $this->Html->css('alpha_landing/styles', array('fullBase' => $cdnBaseUrl));
where $cdnBaseUrl is http://cdn.yourapp.com/
I did not get back the correct url I was expecting.
I was expecting
http://cdn.yourapp.com/css/some.css
But I got back
http://yourapp.com/css/some.css
How can I overcome this problem?
Two solutions:
Simply write a HtmlHelper that can override the default image, css, etc functions
See https://stackoverflow.com/a/9601207/80353 for details
or
you can rewrite the assetUrl function in your AppHelper so that you need not rewrite all the related functions.
public function assetUrl($path, $options = array()) {
$cdnBaseUrl = Configure::read('App.assetsUrl');
$legitCDN = (strpos($cdnBaseUrl, '://') !== false);
if (is_array($path)) {
$path = $this->url($path, !empty($options['fullBase']));
if ($legitCDN) {
return rtrim($cdnBaseUrl, '/') . '/' . ltrim($path, '/');
}
return $path;
}
if (strpos($path, '://') !== false) {
return $path;
}
if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
list($plugin, $path) = $this->_View->pluginSplit($path, false);
}
if (!empty($options['pathPrefix']) && $path[0] !== '/') {
$path = $options['pathPrefix'] . $path;
}
if (
!empty($options['ext']) &&
strpos($path, '?') === false &&
substr($path, -strlen($options['ext'])) !== $options['ext']
) {
$path .= $options['ext'];
}
if (isset($plugin)) {
$path = Inflector::underscore($plugin) . '/' . $path;
}
$path = $this->_encodeUrl($this->assetTimestamp($this->webroot($path)));
if ($legitCDN) {
$path = rtrim($cdnBaseUrl, '/') . '/' . ltrim($path, '/');
}
return $path;
}
This is the sample code for the assetUrl
props to #lorenzo at https://github.com/cakephp/cakephp/issues/2149 for this solution
P.S.: I have rewritten the above as a Plugin.
So you can simply have the AppHelper extend this CDNAppHelper instead.
https://github.com/simkimsia/UtilityHelpers
If you are looking for simplest stuff just update few parameters in end of your core.php file
Configure::write('App.imageBaseUrl', 'http://cdn.yourdomain.com/img/');
Configure::write('App.cssBaseUrl', 'http://cdn.yourdomain.com/css/');
Configure::write('App.jsBaseUrl', 'http://cdn.yourdomain.com/js/');
echo $this->Html->css('alpha_landing/styles', array(
'fullBase' => true,
'pathPrefix'=>$cdnBaseUrl.'css/'));
Try this one

How can I convert validation error field names to input names in CakePHP?

I have a CakePHP (latest version) web app with forms and validation all working properly using traditional postback, but now I'm switching some of the forms to submit via ajax. When there are validation errors, I would like to get them back on the client as JSON formatted like so:
{
"success":false,
"errors":{
"data[User][username]":["This is not a valid e-mail address"],
"data[User][password]":["You must choose a password"]
}}
The keys for the errors array need to correspond to the name attribute on the form fields. We have some prebuilt client script that is expecting JSON formatted in this way. The good news is that this is very close to what the validationErrors object looks like in CakePHP. So I'm currently doing this in my controller:
if ($this->User->save($this->request->data)) {
} else {
if ($this->request->is('ajax')) {
$this->autoRender = $this->layout = false;
$response['success'] = false;
$response['errors'] = $this->User->validationErrors;
echo json_encode($response);
exit(0);
}
}
However, this is what the JSON response looks like:
{
"success":false,
"errors":{
"username":["This is not a valid e-mail address"],
"password":["You must choose a password"]
}}
Note that the errors keys have just the basic database table field names in them. They are not converted into data[User][username] format, which the FormHelper usually takes care of.
Is there some easy way to adjust the array before I return it? I don't want to simply loop through and prepend "data[User]" because that is not robust enough. I'd like some code I can put in one place and call from various controllers for various models. What does FormHelper use to come up with the input name attributes? Can I tap into that? Should I somehow use a JSON view?
That's because that's the way the $validationErrors array is formatted. To obtain the output you want you will have to loop through, there's no way around it.
foreach ($this->User->validationErrors as $field => $error) {
$this->User->validationErrors["data[User][$field]"] = $error;
unset($this->User->validationErrors[$field]);
}
I would suggest instead passing all errors to json_encode(). $this->validationErrors is a combined list of all model validation errors for that request available on the view (compiled after render). You should move your display logic (echoing json) into your view, and loop through it there.
in the view
$errors = array();
foreach ($this->validationErrors as $model => $modelErrors) {
foreach ($modelErrors as $field => $error) {
$errors["data[$model][$field]"] = $error;
}
}
$response['errors'] = $errors;
echo json_encode($response);
This would output something like this:
{
"success":false,
"errors": [
"data[User][username]": "This is not a valid e-mail address",
"data[User][password]": "This is not a valid password",
"data[Profile][name]": "Please fill in the field"
]
}
I have created a small recursive function to create validation error as a string with column name so that can be passed as json object.
/**
* prepare erorr message to be displayed from js
*
* #param array $errors validation error array
* #param stringn $message refernce variable
*
* #return void
*/
public function parseValidationErrors($errors, &$message)
{
foreach ($errors as $columnName => $error) {
$message .= "<strong>$columnName:</strong> ";
foreach ($error as $i => $msg) {
if (is_array($msg)) {
$this->_parseValidationErrors($msg, $message);
} else {
$message .= str_replace("This field", "", $msg . " ");
isset($error[$i + 1]) ? $message .= " and " : $message;
}
}
}
}
and controller code goes like this.
if (!$this->YourModel->saveAll($modelObject)) {
$errors = $this->YourModel->validationErrors;
$message = '';
$this->parseValidationErrors($errors, $message);
$response = array('status' => 'error', 'message' => $message);
}
$response['errors']['User'] = $this->User->validationErrors;

Resources