Hello Joomla Community,
How can a css class be appended to the <body>
from inside a system plugin?
Any advice is appreciated, Thank you.
Hello Joomla Community,
How can a css class be appended to the <body>
from inside a system plugin?
Any advice is appreciated, Thank you.
If you want to inject code in the html code (adding class in body tag) using OnAfterRender
event:
$content= JFactory::getApplication()->getBody();
/* your modification of $content here */
if (preg_match('/(<body.*?)(class *= *"|\')(.*)("|\')(.*>)/', $content))
{
$content = preg_replace(
'/(<body.*?)(class *= *"|\')(.*)("|\')(.*>)/',
'$1$2$3 new_class$4$5',
$content);
}
elseif (preg_match('/(<body.*?)(>)/', $content))
{
$content = preg_replace(
'/(<body.*?)(>)/',
'$1 class ="new_class">',
$content);
}
JFactory::getApplication()->setBody($content);
Editor's note:
According to Joomla Documentation, JResponse::getBody()
and JResponse::setBody()
have been deprecated since Joomla 3.2.
3.2 Use
JApplicationWeb::getBody()
andJApplicationWeb::setBody()
instead
Also, regex can be an unreliable tool for parsing HTML; PHP provides access to several DOM parsers.
$content
then and ensure it's appended to <body>
?
Commented
May 31, 2015 at 21:11
class
and add additional ones like class="first_class second_class third_class"
Using regular expressions to manipulate valid HTML is not advisable because regex is "DOM-unaware" meaning that it doesn't know if it is actually modifying a tag or just text that seems like a tag. Also, unless expressly told to replace only once, it will happily modify multiple matching strings (effectively damaging your content). Finally, using DomDocument is a superior technique because it will intelligently/reliably parse and modify a class attribute regardless of the position of the attribute in the tag and regardless of the character used to quote the value (single or double quotes).
Code: (PHP Demo)
$new_class = "new_class_attr";
$body = JFactory::getApplication()->getBody();
$dom = new DOMDocument;
$dom->loadHTML($body, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$bodytag = $dom->getElementsByTagName('body')->item(0);
if ($bodytag->hasAttribute("class")) {
$new_class = $bodytag->getAttribute("class") . " " . $new_class;
}
$bodytag->setAttribute("class", $new_class);
JFactory::getApplication()->setBody($dom->saveHTML());
And just in case anyone mistakes my endorsement of DOMDocument for fear of regular expressions, here is how I would code it up using refined regex calls.
Code: (PHP Demo) (First Regex Demo) (Second Regex Demo)
$new_class = "new_class_attr";
$body = JFactory::getApplication()->getBody();
$body = preg_replace('~<body[^>]*class *= *["\']\K~', "$new_class ", $body, 1, $count); // add to existing class attribute
if (!$count) {
$body = preg_replace('~<body\K~', " class=\"$new_class\"", $body, 1); // create class with attribute
}
JFactory::getApplication()->setBody($body);
But, again, I don't recommend regex for this job because it will be less stable... Imagine if you had an attribute (and didn't realize it) like specialclass
or data-class
. So look, let's not go down the tit-for-tat rabbit hole of creating a body string that will break the pattern then adjust the pattern to suit the vulnerability -- just use a DOM parser.
onAfterRender()
seems a good trigger to call upon.
Commented
Jan 2, 2019 at 23:12
I thought about this and I came up with a different solution. Assuming your plugin has a field called "class" in the XML.
$app = JFactory::getApplication();
$appParams = $app->getParams();
$class = "";
if ($this->params['class'])
{
$class = $this->params['class'];
}
$pageClass = $appParams['pageclass_sfx'];
if(!empty($pageClass))
{
$class = " " . $this->params['class'];
}
else
{
$class = $this->params['class'];
}
$pageClass = $pageClass . $class;
$app->setParams($pageClass);
As the menu/page class is already added to the body we can just concatenate ur new class
gotten
class is not empty, I am using .=
to append space and the new class. If the gotten
class is not set or falsey, I am not using concatenation -- I am setting the final class value as the new class. Your snippet is saying if the gotten
class is not set or falsey, write the empty string followed by the new class value (no delimiting space required). Hmm, if I haven't explained this well enough, let me know and I'll try to say it differently.
Commented
Jul 20, 2019 at 13:11
.=
instead of just =
. I get what it does, but I've usually seen it used when multiple variables are assigned in a row. But my main point is in the else statement I seem to be looking up params just to assign an empty class. But we already checked if it was empty earlier, so really no need. Could assign ""
or just not assign anything. Is that right? Or have I still gotten confused
.=
(concatenating assignment) streamlines the codes by removing the need to write the variable name on both sides of the assignment. And in the else, only assignment is used for "tinfoil hat" reasons. (For instance, someone writes an invalid class like class="0"
-- empty()
evaluates 0
as true, and concatenating will create class="0 newClassName"
) Beyond this wacky fringe case, you could use concatenating assignment in both branches. If you do, you could entertain the idea of writing an inline (ternary) condition statement -- but that will make a long line.
Commented
Jul 21, 2019 at 21:46