I have a message in my solution.
The problem is when I want to add a mail (html + css), the main css is changing.
This is a test concluded:
And this is an bad example (other mail):
I am putting an e-mail html like this:
<div class="inbox-view">
<?php
function isHTML($string) {
if ($string != strip_tags($string)) {
return true;
} else {
return false;
}
}
if (isHTML($mail[0]->BODY)) {
echo $mail[0]->BODY;
} else {
echo str_replace("\n", "<br/>", $mail[0]->BODY);
}
?>
</div>
I think you can consider using shadow DOM here as it will separate the css from other code on the page so that different parts do not clash. But i am afraid that you can do it using javscript only.
You can find the details on how to create shadow DOM from here.
https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_shadow_DOM
How about using more specific selectors.
e.g. div#first p#myParagraph span.bolder
Or use !important as last resort.
Related
I need to fetch a remote page, modify some elements (using 'PHP Simple HTML DOM Parser' library for that) and output modified content.
There's a problem with remote pages that don't have full URLs in their source, so CSS elements and images are not loaded. Sure, it doesn't stop me from modifying elements, but the output looks bad.
For example, open https://www.raspberrypi.org/downloads/
However, if you use code
$html = file_get_html('http://www.raspberrypi.org/downloads');
echo $html;
it will look bad. I tried to apply a simple hack, but that helps just a little:
$html = file_get_html('http://www.raspberrypi.org/downloads');
$html=str_ireplace("</head>", "<base href='http://www.raspberrypi.org'></head>", $html);
echo $html;
Is there any way to "instruct" script to parse all links from $html variable from 'http://www.raspberrypi.org'? In other words, how to make raspberrypi.org to be the "main" source of all images/CSS elements fetched?
I daon't know how to explain it better, but I believe you got an idea.
I just have tried this on local, and I've noticed(in the source code) the link tags in the HTML are like this:
<link rel='stylesheet' href='/wp-content/themes/mind-control/js/qtip/jquery.qtip.min.css' />
It obviously requires a file that should be in my local directory (like localhost/wp-content/etc.../).
The href of the link tags must be something like
<link rel='stylesheet' href='https://www.raspberrypi.org/wp-content/themes/mind-control/js/qtip/jquery.qtip.min.css' />
So what you probably want to do is find all link tags and add in their href attribute "https://www.raspberrypi.org/" in front of the rest.
EDIT: Hey I've actually made the style work, try this code:
$html = file_get_html('http://www.raspberrypi.org/downloads');
$i = 0;
foreach($html->find('link') as $element)
{
$html->find('link', $i)->href = 'http://www.raspberrypi.org'.$element->href;
$i++;
}
echo $html;die;
Since only Nikolay Ganovski offered a solution, I wrote a code which converts partial pages into full by looking for incomplete css/img/form tags and making them full. In case someone needs it, find the code below:
//finalizes remote page by completing incomplete css/img/form URLs (path/file.css becomes http://somedomain.com/path/file.css, etc.)
function finalize_remote_page($content, $root_url)
{
$root_url_without_scheme=preg_replace('/(?:https?:\/\/)?(?:www\.)?(.*)\/?$/i', '$1', $root_url); //ignore schemes, in case URL provided by user was http://domain.com while URL in source is https://domain.com (or vice-versa)
$content_object=str_get_html($content);
if (is_object($content_object))
{
foreach ($content_object->find('link.[rel=stylesheet]') as $entry) //find css
{
if (substr($entry->href, 0, 2)!="//" && stristr($entry->href, $root_url_without_scheme)===FALSE) //ignore "invalid" URLs like //domain.com
{
$entry->href=$root_url.$entry->href;
}
}
foreach ($content_object->find('img') as $entry) //find img
{
if (substr($entry->src, 0, 2)!="//" && stristr($entry->src, $root_url_without_scheme)===FALSE) //ignore "invalid" URLs like //domain.com
{
$entry->src=$root_url.$entry->src;
}
}
foreach ($content_object->find('form') as $entry) //find form
{
if (substr($entry->action, 0, 2)!="//" && stristr($entry->action, $root_url_without_scheme)===FALSE) //ignore "invalid" URLs like //domain.com
{
$entry->action=$root_url.$entry->action;
}
}
}
return $content_object;
}
Consider the following two blocks of code.
1.
<?php
$c = 5;
if ($c < 1){
echo '<span style=" color:#F00;font-size:12px;float:right;">This one is red</span>';
} else
echo '<span style=" color:#F00;font-size:12px;float:right;"> C is smaller</span>';
?>
2.
<?php
$c = 5;
if ($c < 1){
echo '<span class="classC">This one is red</span>';
} else
echo '<span class="classC"> C is smaller</span>';
?>
CSS
.classC
{
color:#F00;
font-size:12px;
float:right;
}
Which one is effective and more correct?
Always use CSS Classes. It's better for code reusability.
You should also consider putting your CSS code in an external CSS file. It keeps your HTML code clean, and this way, the browser can cache the CSS file, so it's faster.
The second one is definitely the preferred way to do this.
The second way is also more effective. They are both technically correct. But it is better to put all your styles in CSS, then to have it stored in html/php files.
I am writing a simple HTML email design editor in PHP and also show a demo of how this will look.
I think it would also be very useful to show the user how this will look in an email client such as gmail with images turned off.
What is my best approach for this? Anybody know how this is done in gmail/hotmail etc?
Do I simple remove img -> src and css background: url with a reg expression?
I would like to remove the background parts from:
background="url" used in tables and
background-image:url(url); used inline css
I found this question which has the same kind of idea, although I would like to actually remove the img and backrgound-images from the HTML text.
Or could this code be modified to work with background images also?
I would also suggest using PHP DOM instead of regex, which are often inaccurate. Here is an example code you could use to strip all the img tags and all the background attributes from your string:
// ...loading the DOM
$dom = new DOMDocument();
#$dom->loadHTML($string); // Using # to hide any parse warning sometimes resulting from markup errors
$dom->preserveWhiteSpace = false;
// Here we strip all the img tags in the document
$images = $dom->getElementsByTagName('img');
$imgs = array();
foreach($images as $img) {
$imgs[] = $img;
}
foreach($imgs as $img) {
$img->parentNode->removeChild($img);
}
// This part strips all 'background' attribute in (all) the body tag(s)
$bodies = $dom->getElementsByTagName('body');
$bodybg = array();
foreach($bodies as $bg) {
$bodybg[] = $bg;
}
foreach($bodybg as $bg) {
$bg->removeAttribute('background');
}
$str = $dom->saveHTML();
I've selected the body tags instead of the table, as the <table> itself doesn't have a background attribute, it only has bgcolor.
To strip the background inline css property, you can use the sabberworm's PHP CSS Parser
to parse the CSS retrieved from the DOM: try this
// Selecting all the elements since each one could have a style attribute
$alltags = $dom->getElementsByTagName('*');
$tags = array();
foreach($alltags as $tag) {
$tags[] = $tag;
} $css = array();
foreach($tags as &$tag) {
$oParser = new CSSParser("p{".$tag->getAttribute('style')."}");
$oCss = $oParser->parse();
foreach($oCss->getAllRuleSets() as $oRuleSet) {
$oRuleSet->removeRule('background');
$oRuleSet->removeRule('background-image');
}
$css = $oCss->__toString();
$css = substr_replace($css, '', 0, 3);
$css = substr_replace($css, '', -2, 2);
if($css)
$tag->setAttribute('style', $css);
}
Using all this code togheter, for example if you have a
$string = '<!DOCTYPE html>
<html><body background="http://yo.ur/background/dot/com" etc="an attribute value">
<img src="http://your.pa/th/to/image"><img src="http://anoth.er/path/to/image">
<div style="background-image:url(http://inli.ne/css/background);border: 1px solid black">div content...</div>
<div style="background:url(http://inli.ne/css/background);border: 1px solid black">2nd div content...</div>
</body></html>';
The PHP will output
<!DOCTYPE html>
<html><body etc="an attribute value">
<div style="border: 1px solid black;">div content...</div>
<div style="border: 1px solid black;">2nd div content...</div>
</body></html>
In order to fully mimic the behavior of gmail or similar web mails would be to replace the tags, and background: css attributes accordingly so that they display a placeholder, making clear to the user that here lies an image.
Since usually the message is being loaded in an iframe I believe that your best guess, would be to clean the message server side removing all unwanted tags and replacing images accordingly on preview.
I will agree with Michal that it is not wise to use just regex to validate your HTML and you probably should traverse the DOM tree just to be safe.
Why don't you take a look at washtml by Frederic Motte used by roundcube to get you started?
Using regular expressions to parse html is usually not recommended.
I think a better approach would be to parse the html server-side, and manipulate it to remove the images or the image src attributes. A library I've had success with is http://simplehtmldom.sourceforge.net/, but I think you can use official PHP DOM extensions.
The removal of background images might be more tricky. You might have to use something like http://www.pelagodesign.com/sidecar/emogrifier/ to apply something like {background: none} to the html elements. However, CSS background images are not supported in the latest versions of Microsoft Outlook, so I would recommend not using them at all from the get-go in order to have the emails to be consistent for most email clients.
Like tkone mentioned: perhaps JavaScript / jQuery is the answer.
This will look at all images in your preview area and change the source to a placeholder image. The 'placeholder' class sets the background image to the placeholder as well
jQuery
$("#previewArea img").each(function(){
$(this).attr("src","placeholder.jpg");
$(this).addClass("hideBG");
});
CSS
.hideBG{
background: url("placeholder.jpg");
}
Not tested, but should work - depending on your setup and needs.
I've asked a similar question (in solution, not actual problem): How to strip specific tags and specific attributes from a string? (Solution)
It's a server side library which cleans (and formats) HTML input according to predefined settings. Have it remove any src attributes and all background properties.
You could always do this on the client end as well.
Using this hypothetical code, you should be able to do something like this, pretending that modern browsers all work the same: (or use jQuery or something)
var email;
var xhr = new XMLHttpRequest();
xhr.open('GET', URL_FOR_EMAIL, true);
xhr.onreadystatechange = function(event){
if(xhr.readyState === 4 && xhr.status === 200){
email = HTMLParser(xhr.responseText);
}
}
var imgs = email.getElementsByTagName('img');
for(var i = 0; i > imgs.length; i++){
email.removeChild(imgs[i]);
}
// attach the email body to the DOM
// do something with the images
HTMLParser from MDN
function HTMLParser(aHTMLString){
var html = document.implementation.createDocument("http://www.w3.org/1999/xhtml", "html", null),
body = document.createElementNS("http://www.w3.org/1999/xhtml", "body");
html.documentElement.appendChild(body);
body.appendChild(Components.classes["#mozilla.org/feed-unescapehtml;1"]
.getService(Components.interfaces.nsIScriptableUnescapeHTML)
.parseFragment(aHTMLString, false, null, body));
return body;
},
I think that the best way to do it and keep the change reversible its using a tag who not process the "src" attribute.
Ex: Change all the "img" with "br"
So print the filtered HTML 1st and reverse it with ajax, search for all the br with a src attribute.
I have some numbers, and depending on the number's value I need to highlight a string which is in a table. The table is not in database.
Does anyone know how to highlight text in php?
PHP won't "highlight" text, that is done with CSS/HTML. You only use PHP to output the correct HTML.
Something like this:
$my_num = 9;
foreach ($my_array_of_numbers as $num)
{
// See if $num matches $my_num, if so - add the "highlight"
// class to the HTML element we're using
$css_class = ($num == $my_num) ? 'highlight' : '';
echo '<td class="'.$css_class.'">'.$num.'</td>';
}
Then in your CSS:
.highlight {
background:yellow;
}
You may also want to consider using javascript for this, which can handle the task after the HTML has already been generated.
Presentation, like highlighting, is done in CSS, not PHP.
My advice would be to create a CSS class which has a background-color defined to be used as highlighting. Then, in PHP, you can conditionally wrap your string in <span> tags which have that class, so the text would be highlighted.
CSS:
.highlight {
background-color: #FF0;
}
PHP:
if (highlightCondition) {
echo '<span class="highlight">' . $string . '</span>';
} else {
echo $string;
}
How can I check if links from a string variable are external? This string is the site content (like comments, articles etc).
And if they are, how do I append a external value to their rel attribute? And if they don't have this attribute, append rel="external" ?
A HTML parser is appropriate for input filtering, but for modifying output you'll need the performance of a simpleminded regex solution. In this case a callback regex would do:
$html = preg_replace_callback("#<a\s[^>]*href="(http://[^"]+)"[^>]*>#",
"cb_ext_url", $html);
function cb_ext_url($match) {
list ($orig, $url) = $match;
if (strstr($url, "http://localhost/")) {
return $orig;
}
elseif (strstr($orig, "rel=")) {
return $orig;
}
else {
return rtrim($orig, ">") . ' rel="external">';
}
}
You'll probably need more fine-grained checks. But that's the general approach.
Use an XML parser, like SimpleXML. Regex isn't made to do XML/HTML parsing, and here's a perfect explanation of what happens when you do: RegEx match open tags except XHTML self-contained tags.
Parse the input as XML, use the parser to select the required elements, edit their properties using the parser, and spit them back out.
It'll save you a headache, as regex makes me cry...
Here's my way of doing this (didn't test it):
<?php
$xmlString = "This is where the HTML of your site should go. Make sure it's valid!";
$xml = new SimpleXMLElement($xmlString);
foreach($xml->getElementsByTagName('a') as $a)
{
$attributes = $a->attributes();
if (isThisExternal($attributes['href']))
{
$a['rel'] = 'external';
}
}
echo $xml->asXml();
?>
It might be easier to do something like this on the client side, using jQuery:
<script type="text/javascript">
$(document).ready(function()
{
$.each($('a'), function(idx, tag)
{
// you might make this smarter and throw out URLS like
// http://www.otherdomain.com/yourdomain.com
if ($(tag).attr('href').indexOf('yourdomain.com') < 0)
{
$(tag).attr('rel', 'external');
}
});
});
</script>
As Craig White points out though, this doesn't do anything SEO-wise and won't help users who have JavaScript disabled.