HtmlPurifier - keeps removing my tables - what is the right config? - config

I have a table that keeps beeing removed by html purifier
the content is an email that needs to be populated
<table>
<thead>
<th>fieldA</th>
<th>fieldB</th>
<th>fieldC</th>
<th>fieldD</th>
</thead>
<tbody>
<!--[[[order_list]]]-->
</tbody>
</table>
where < ! - - [[[order_list]]] - - > is replaced at runtime, but htmlpurifier keeps removing my table (the comments are there for ckeditor, otherwise it gets removed)
I tried to disable the AutoFormat.RemoveEmpty :
$config = \HTMLPurifier_Config::createDefault();
$config->set('AutoFormat.RemoveEmpty', false);
Validator::$purifier = new \HTMLPurifier($config);
$val=Validator::$purifier->purify($data);
it does not work at all, the table yet gets removed
how can I fix this ?
thanks
[edit: tried this so far...]
$config = \HTMLPurifier_Config::createDefault();
// $config->set('AutoFormat.RemoveEmpty', false);
// ----------------------------------------------- from stackoverflow
$def = $config->getHtmlDefinition(true);
$element = $def->addElement(
'tbody',
false,
'Flow', // <-- here is the change - we're not requiring 'tr' any more
'Common',
[
'align' => 'Enum#left,center,right,justify,char',
'charoff' => 'Length',
'valign' => 'Enum#top,middle,bottom,baseline',
]
);
$config->set('HTML.AllowedComments', array('[[[[order_list]]]'));
Validator::$purifier = new \HTMLPurifier($config);

The problem you're encountering is that tbody cannot be empty by the HTML standard ("Each row group must contain at least one row") (and much of HTML Purifier's security comes from strategically requiring the HTML to be standards-compliant), a comment does not count as content, and it's thus removed. That said, the "living" standard of HTML doesn't require this ("Zero or more tr ..."), so it should be safe to fiddle with.
In any case, you can see HTML Purifier's requirement in the file library/HTMLPurifier/HTMLModule/Tables.php:
$this->addElement('tbody', false, 'Required: tr', 'Common', $cell_align);
If you want to overwrite the way HTML Purifier treats tbody elements, check out the Customize documentation. Your solution would look a little like this (although this example does not cache the HTML definition, you may want to fiddle with yours until yours does):
require_once (__DIR__ . '/library/HTMLPurifier.auto.php');
$dirty_html = '<table>
<thead>
<th>fieldA</th>
<th>fieldB</th>
<th>fieldC</th>
<th>fieldD</th>
</thead>
<tbody>
<!--[[[order_list]]]-->
</tbody>
</table>';
$config = HTMLPurifier_Config::createDefault();
$def = $config->getHtmlDefinition(true);
$element = $def->addElement(
'tbody',
false,
'Flow', // <-- here is the change - we're not requiring 'tr' any more
'Common',
[
'align' => 'Enum#left,center,right,justify,char',
'charoff' => 'Length',
'valign' => 'Enum#top,middle,bottom,baseline',
]
);
$purifier = new HTMLPurifier($config);
$clean_html = $purifier->purify($dirty_html);
This spits out (though I removed the whitespace):
<table><thead></thead><tbody></tbody></table>
If the result still does not seem right to you because thead was emptied out, know that thead was emptied because you have "stray" th tags in it - they're not wrapped by tr. That is required to be standards-compliant even by the "living" standard (as it only permits tr children), but you could of course also change that behaviour by tweaking HTML Purifier if you want. That might have unintended consequences for browser rendering, though, be careful!
That said, this alone will still kill the <!-- [[[order_list]]] --> comment, since HTML Purifier by default disallows all comments for security reasons (there've historically been quite a few browser exploits busting out of HTML comments, although not so much in recent years).
To fix this, you can whitelist the specific comment you want to preserve like this:
...
$config = HTMLPurifier_Config::createDefault();
$config->set('HTML.AllowedComments', ['[[[order_list]]]']);
$def = $config->getHtmlDefinition(true);
...
This will spit out (though I again removed the whitespace):
<table><thead></thead><tbody><!--[[[order_list]]]--></tbody></table>

Related

Query returns Object(Builder), undefined property

I have following code
public function detailCustomer(Customer $customer)
{
$vehicles = DB::table('vehicles')
->selectRaw('*')
->where('cust_id', '=', $customer->id);
return view('customers.detail', array('customer' => $customer, 'vehicles' => $vehicles));
}
Where table vehicles consists of:
spz
cust_id <FK> //this is foreign key to customer->id
type
brand
In the customers.detail view, I tried to use following code to show data, but I get this error:
Undefined property: Illuminate\Database\PostgresConnection::$spz
Code:
#if (count($vehicles) > 0)
<?php $i = 1; ?>
#foreach ($vehicles as $vehicle)
<?php $i++; ?>
<td>{{$vehicle->spz}}</td>
<td>{{$vehicle->type}}</td>
<td>{{$vehicle->brand}}</td>
#endforeach
#endif
I have read this topic but seems it's not my problem because I use foreach to iterate through the object but seems I do not get the object from database into my $vehicles variable, because in the error page, it shows also something like this:
'customer' => object(Customer), 'vehicles' => object(Builder)
What makes me think that customer gets its object correctly, but vehicles gets Builder?? Really no idea what is wrong there. Any ideas?
Just to describe what am I doing in the project that I work on, I have a customer detail page where I click a button to add a vehicle to his detail page (profile page) and I send customer id as parameter to the function where I add vehicle into database, which works (vehicle is added correctly with the customer id). Now that problem shows up when I want to show detail page with vehicle information like the code above shows.
Hope its clear enough. Thanks for suggestions.
Try to add ->get() or ->paginate($YOUR_LIMIT_ONE_PAGE) in your Controller
public function detailCustomer(Customer $customer)
{
$vehicles = DB::table('vehicles')
->selectRaw('*')
->where('cust_id', '=', $customer->id)->get();
// ->where('cust_id', '=', $customer->id)->paginate($YOUR_LIMIT_ONE_PAGE);
return view('customers.detail', array('customer' => $customer, 'vehicles' => $vehicles));
}
And try to replace your foreach to this forelse
#forelse ($vehicles as $index => $vehicle)
<tr>
<td>{{$index}}</td>
<td>{{($vehicle->spz !== null) ? $vehicle->spz : '-'}}</td>
<td>{{($vehicle->type !== null) ? $vehicle->type : '-'}}</td>
<td>{{($vehicle->brand !== null) ? $vehicle->brand : '-'}}</td>
</tr>
#empty
<td colspan='4'>Data not found</td>
#endforelse

Html2pdf hindi support

I am using html2pdf in my Yii application to generate Pdf's, This is my code
Some 'Hindi' or 'Tamil' words are need to show in the pdf.
How can I get this?
sample code and generated Pdf are below, it shows either '?' or squares
$html2pdf = Yii::app()->ePdf->HTML2PDF();
$html2pdf = new HTML2PDF('L', 'A4', 'en');
$html2pdf->setDefaultFont('freesans');
$html2pdf->WriteHTML($this->renderPartial('attentancepdf', array(), true));
$html2pdf->Output($batch_name);
<span align="center"><h4><?php echo Yii::t('app','CLASS STUDENT ATTENDANCE'); ?></h4></span>
I have removed the "h4" tag then its worked , I dont know how!?
Also 'b' tag !!
<td width="150"><b><?php echo Yii::t('app','Batch'); ?></b></td>

Yii: How to work with translate Yii::t() and hyperlinks

I have many lines similar to this in my code:
echo Yii::t('forms','Would you like to create a new item?');
where I want to hyperlink just around "create a new item", as an example.
Here are some alternatives that I've thought about:
Split the URL into 2 translated strings, surrounded by a hyperlink:
echo Yii::t('forms','Would you like to').' '.Yii::t('forms','create a new item').'?';
Use placeholders, as described in the Yii documentation ( http://www.yiiframework.com/doc/guide/1.1/en/topics.i18n Although hyperlinks aren't given as an explicit example):
echo Yii::t('forms','Would you like to {url}create a new item',array('{url}'=>"<a href='/new_item'>")).'</a>?';
There's probably an easier way to do this, but I've been unable to discover the preferred method...what's the best way to build translated strings that include URLs?
I suggest to you this solution:
echo Yii::t(
'forms',
'Would you like to {link:create}create a new item{/link}?',
array(
'{link:create}'=>'<a href="/new_item">',
'{/link}'=>'</a>',
)
);
The benefit is if you want put id, class, onclick and more anything in a tag you can do it. and so the translate string in clear.
Note that create in {link:create} is just a ideal string that pointer to hyperlink string.
Another advanced sample:
echo Yii::t(
'forms',
'Would you like to {link:create}create a new item{/link}? And you can {link:delete}delete the item{/link}.',
array(
'{link:create}'=>'<a href="/new_item" class="button">',
'{link:delete}'=>'<a href="#" id="item-21" onclick="delete(21);">',
'{/link}'=>'</a>',
)
);
The link may have different placement (beginning, middle or end) and label in the translated string depending on a target language. Therefore, you should use placeholder only for url:
echo Yii::t(
'forms',
'Would you like to create a new item?',
array('{url}' => '/new_item')
);
Use following if you have a dynamic uri:
echo Yii::t(
'forms',
'Would you like to create a new item?',
array(':url'=>'/new_item')
);
Or:
echo Yii::t(
'forms',
'Would you like to create a new item?',
);
Or if you want to pass other dynamic attributes other than the url, use the following:
echo Yii::t(
'forms',
'Would you like to <a :linkAttr>create a new item?</a>',
array('linkAttr'=>'href="/new_item" id="link-id" class="link-class"')
);
I think this is a better solution:
echo Yii::t(
'forms',
'Would you like to {action}?'
[
'action' => Html::a(
Yii::t('forms', 'create a new item'),
['controller/action']
)
]
);
Benefits of this solution
You can use helpers to generate your link
You can modify your html code without modifing the translations
Whoever will be doing translations doesn't need to know anything about html and they can't mess the html code.

Youtube api get latest upload thumbnail

I am looking to returning the video-thumbnail of the latest uploaded video from my channel, and display it on my website.
Anyone know how I can do a minimal connection trough api and get only the thumbnail?
Thanks!
-Tom
REVISED!!
Using Cakephp, this is how I did it (thanks dave for suggestions using zend);
controller:
App::import('Xml');
$channel = 'Blanktv';
$url = 'https://gdata.youtube.com/feeds/api/users/'.$channel.'/uploads?v=2&max-results=1&orderby=published';
$parsed_xml =& new XML($url);
$parsed_xml = Set::reverse($parsed_xml);
//debug($parsed_xml);
$this->set('parsed_xml',$parsed_xml);
View;
$i=0;
foreach ($parsed_xml as $entry)
{
echo '<a href="/videokanalen" target="_self">
<img width="220px" src="'.$entry['Entry']['Group']['Thumbnail'][1]['url'] .'">
</a>';
}
Now the only thing remaining is to cache the feed call someway.. Any suggestions???
-Tom
here is a quick dirty way of doing it without really touching the api at all.
I'm not suggesting it's best practice or anything and I'm sure there are smarter ways but it definitely works with the current Youtube feed service.
My solution is PHP using the Zend_Feed_Reader component from Zend Framework, if you need a hand setting this up if you're not familiar with it let me know.
Essentially you can download version 1.11 from Zend.com here and then make sure the framework files are accessible on your PHP include path.
If you are already using Zend Framework in an MVC pattern you can do this in your chosen controller action:
$channel = 'Blanktv'; //change this to your channel name
$url = 'https://gdata.youtube.com/feeds/api/users/'.$channel.'/uploads';
$feed = Zend_Feed_Reader::import($url);
$this->view->feed = $feed;
Then you can do this in your view:
<h1>Latest Video</h1>
<div>
<?php
$i=0;
foreach ($this->feed as $entry)
{
$urlChop = explode ('http://gdata.youtube.com/feeds/api/videos/',$entry->getId());
$videoId = end($urlChop);
echo '<h3>' . $entry->getTitle() . '</h3>';
echo '<p>Uploaded on: '. $entry->getDateCreated() .'</p>';
echo '<a href="http://www.youtube.com/watch?v=' . $videoId .'" target="_blank">
<img src="http://img.youtube.com/vi/' . $videoId .'/hqdefault.jpg">
</a>';
$i++;
if($i==1) break;
}
?>
</div>
otherwise you can do:
<?php
$channel = 'Blanktv'; //change this to your channel
$url = 'https://gdata.youtube.com/feeds/api/users/'.$channel.'/uploads';
$feed = Zend_Feed_Reader::import($url);
?>
<h1>Latest Video</h1>
<div>
<?php
$i=0;
foreach ($feed as $entry)
{
$urlChop = explode ('http://gdata.youtube.com/feeds/api/videos/',$entry->getId());
$videoId = end($urlChop);
echo '<h3>' . $entry->getTitle() . '</h3>';
echo '<p>Uploaded on: '. $entry->getDateCreated() .'</p>';
echo '<a href="http://www.youtube.com/watch?v=' . $videoId .'" target="_blank">
<img src="http://img.youtube.com/vi/' . $videoId .'/hqdefault.jpg">
</a>';
$i++;
if($i==1) break;
}
?>
</div>
With the latter method you'll likely need to use a php require statement for the Zend_Feed_Reader files etc....
Hope this helps, like I say let me know if you need a hand.
All the best,
Dave
UPDATE: In response to your comments about caching
Hi Tom, here is another quick and dirty solution which doesn't use cache but may be very quick to implement.
The reason I didn't go with a caching component is because I figured a simple db solution would suffice under the circumstances. I also thought having to pull the feed to compare whether it was new or not wouldn't be the most economical for you.
You could automate this process to be run automatically at specified times but if you don't want to automate the process and don't mind clicking a link to update the video manually you could trigger it that way.
My solution is again based on ZF but since you were ok hacking it into something useful with cakephp you should have no problem doing the same here.
First set up a new table (assuming a MySQL db):
CREATE TABLE `yourdbname`.`latestvid` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY COMMENT 'Unique identifier',
`videoId` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'Video id',
`videoTitle` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'Video title',
`uploadDate` VARCHAR( 100 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT 'Video upload date'
) ENGINE = INNODB CHARACTER SET utf8 COLLATE utf8_general_ci;
INSERT INTO `yourdbname`.`latestvid` (`id`, `videoId`, `videoTitle`, `uploadDate`) VALUES (NULL, '--', '--', '--');
This will create a table for your latest video info for use in your template however the default values I've set up will not work with your template for obvious reasons.
You could then do something similar to this:
public function updateAction()
{
$this->_helper->viewRenderer->setNoRender(); // disable view
$this->_helper->layout()->disableLayout(); // disable layout
$user = 'Blanktv'; // insert your channel name
$url = 'https://gdata.youtube.com/feeds/api/users/'.$user.'/uploads';
$feed = Zend_Feed_Reader::import($url);
if(!$feed)
{
die("couldn't access the feed"); // Note: the Zend component will display an error if the feed is not available so this wouldn't really be necessary for ZF
}
else
{
$i=0;
foreach ($feed as $entry)
{
$urlChop = explode ('http://gdata.youtube.com/feeds/api/videos/',$entry->getId());
$videoId = end($urlChop);
$videoTitle = $entry->getTitle();
$uploadDate = $entry->getDateCreated();
// use your preferred method to update the db record where the id = 1
$i++;
if($i==1) break;
}
}
}
Maybe have a go and let me know how you get on?
You'd just need to tweak the template so you'd get the variables from the database instead of Youtube with the exception of the thumbnail.
I suppose you could always take that approach further and actually store images etc since the thumbnail is still being pulled from Youtube and may slow things down.
You could set up a script to copy the thumbnail to your own server and store the path in the db or use a standard thumbnail if you are running a series of videos for which you require standard branding - anyway hope it helps.
:-D
Dave

Filtering results based on a distinct column or field value

I have an MVC 3 application running against an MS SQL 2008 database with a table named Documents. Documents are broken down by paragraph in the database. The Documents table has a DocText column containing the text of each paragraph, a DocTitle column containing the document title. My MVC 3 app has a search function that can search for a word or phrase in the DocText column or in the DocTitle column. Everything works fine except that if a particular document has the search word appearing in multiple paragraphs, my List returns multiple instances of that document. For example, if the user searches the word "Budget" and one of THE documents has the word "budget" in four different paragraphs, my returned list has that document listed four times.
What I want to achieve is to list each document that has the searched word. I only want to list the document by Title once, regardless of the number of times the search word appears in that document. The only column that is truly unique is the RecordID column, a primary key.
My controller:
public class SearchController : Controller
{
private GUICEEntities4 db = new GUICEEntities4();
//
// GET: /Search/
public ActionResult Index(string Keyword)
{
#region Keyword Search
if (!String.IsNullOrEmpty(Keyword)) {
var SearchDoc = db.Documents.Where(r => r.DocText.ToUpper().Contains(Keyword.ToUpper()) || r.Title.ToUpper().Contains(Keyword.ToUpper()) || r.DocNum.ToUpper().Contains(Keyword.ToUpper()));
ViewBag.CurrentKeyword = String.IsNullOrEmpty(Keyword) ? "" : Keyword;
return View(SearchDoc.ToList());
}
else{
return View();
}
#endregion
}
}
My View has the following:
#foreach (var item in Model) {
<tr>
<td>
<strong>AFI #Html.DisplayFor(modelItem => item.DocNum): #Html.DisplayFor(modelItem => item.Title)</strong>
<br />
#Html.DisplayFor(modelItem => item.DocSummary)
<br />
<span class="complianceitems">Number of compliance items:</span> (TBD)
</td>
<td>
<a href="/Documents/Index/#(Html.DisplayFor(modelItem => item.DocNum))">Checklist
Generator</a><br />
<a href="/UploadedDocs/#Html.DisplayFor(modelItem => item.DocFileName)" target="_blank">
Download PDF</a>
</td>
Any suggestions on how I can achieve my goal?
ADDED: Each document can be identified by the DocNum column which has a unique document number for that particular document. I've tried to iterate through the List to pull out each unqiue DocNum and then try to make that DocNum not appear again in the loop...but I was not successful.
The following SQL statement gives me the results I need. The statement assumes that the search word is "budget". I don't know how to get the same results using EF. Any suggestions?
SELECT DISTINCT DocNum, Title FROM Documents
WHERE
DocText LIKE '%budget%'
OR
Documents.Title LIKE '%budget%'
OR
DocNum LIKE '%budget%'
The issue here is in your EF query and not anything related to MVC. It's been a while since I've actively used EF but the simplest way would probably be to return just the RecordIds first.
var recordIds= db.Documents
.Where(r =>
r.DocText.ToUpper().Contains(Keyword.ToUpper()) ||
r.Title.ToUpper().Contains(Keyword.ToUpper()) ||
r.DocNum.ToUpper().Contains(Keyword.ToUpper()))
.Select(d => d.RecordId)
.Distinct();
I'm not exactly sure what you will do with each individual record from then on as there isn't enough information in your question. But this should help.
Update:
var foundDocs = new List<YourType>();
recordIds.ToList().ForEach(r => foundDocs.Add(db.TblDocLists.Single(l => l.TheDocNum == r)));
//I must point out that I'm not familiar with the latest versions of EF so this might be overkill.