Archive for the ‘code’ Category
Bug in Concrete 5
Tuesday, May 4th, 2010PDFGenerator for Concrete 5
Sunday, May 2nd, 2010After Georg (below) commented, I felt inspired to polish the code into usable Alpha state.
IdeaDay PDF Generator (v3.0 Alpha)
This Concrete 5 package encapsulates mpdf and makes it easy to create PDF versions of your pages.
It was written by ideaday.de
Contact: http://www.ideaday.de/kontakt
mpdf was written by Ian Back (it makes itself heavy use of other OSS projects).
We use Version Version 4.4, released 24/03/10
See the other files for Licensing and Version information. (This blog entry is just a formatted README.txt)
Download
Get your package here:
- Download the newest version from my download page
Installation
1) download the ZIP
2) unzip it
3) upload the folder ideaday_pdf_generator in your packages folder
4) install it using the Dashboard
Usage
Add an Icon / link to the page(s) you want to generate the PDF from, including the query parameter ?ipg_mode=pdf
i.e. http://www.example.com/this/is/a/page/?ipg_mode=pdf
Feel free to edit the default settings in /helpers/pdf.php:
$options['ipg_filename'] = 'website.pdf';
$options['ipg_mode'] = 'I';
$options['ipg_header'] = ";//'{PAGENO}'; //You could set up your own header here.
$options['ipg_footer'] = ";
To modify the way the PDF is shown (streamed / forced download) you currently have to edit the line
$pdf->showPDF();
and change it to the appropriate function.
Functions
public function getHTML()
Returns the actual HTML used to build the PDF.
public function getPDF()
Returns the PDF as a string.
public function streamPDF()
Streams the PDF.
public function showPDF()
Displays the PDF in the browser
Known limitations
- Floating images are rendered on top of the text instead floating beside it. If you specifically set the image size in pixels (IMG width / height), it will render correctly.
- I have exposed only a very limited subset of the MPDF package, i.e. it renders in DIN A4, instead of letter, etc.
- You have to add the icon/link, and tweak the source to change settings. I plan to add a block which will display a link and allow you to set up settings on each page individually.
Concrete5 does not update correctly?
Friday, April 30th, 2010Starting with the newest version of Concrete, 5.4, updates have been integrated into the web interface.
For me, this update process suddenly did not work anymore correctly:
After running the upgrade script, it would still show that we were using the old version of the code and offer to upgrade again – as if nothing had happened inbetween.
The solution to this problem lies in your config/site.php – look at the very end of it, it will contain a statement similar to this one:
<?php define('DIRNAME_APP_UPDATED', 'concrete5.4.0.5');?>
My config/site.php contained two statements – one having the previous version, one the newest version of the update.
Try removing them both and running the upgrade once again. It will work this time – no upgrade will be offered again.
Does the AdoDB C extension speed up Concrete?
Tuesday, April 6th, 2010My experiments with a sufficiently complex page (www.vitanit.com) have shown: no. There even may be a slight time penalty.
To enable it, you'll have to download it, compile it, and add the following to your php.ini:
extension=adodb.so
How do you know that it's installed? phpinfo() will show something like:
ADOdbInfo Extension requires ADOdb classes
Download http://php.weblogs.com/adodb
API Version 5.04
It might be useful for the IdeaDay Import Wizard – which relies heavily on the database.
Concrete5 File Uploads with Forms
Sunday, April 4th, 2010If you use Form Helpers and add a file upload there's a gotcha, which can drive you insane if you don't know about it:
The form encoding type has to be set to multipart, like this:
<form method="post" enctype="multipart/form-data" action="<?php echo $this->action('submit')?>">
If you don't do this, your file will never turn up, and you will be debugging like mad and not understanding where the problem is :-)
Adding aliases to Concrete5 pages programmatically.
Sunday, April 4th, 2010Suppose, you want to provide legacy URLs when porting the page to a new version. I need to do this, as I am writing the IdeaDay Import Wizard.
An example:
-
The new page will be under /my/new/page
-
You want a legacy alias under /legacy_path/legacy_name
There is a function $page->addCollectionAlias($c); which will alias your Collection UNDER another Collection (essentially adding the page with the same name elsewhere). We could use this, but would need to work around the new page's name and cHandle (which will change: page != legacy_name)
How does C5 handle this internally, when editing page aliases?
/concrete/startup/process.php contains the code (look for else if ($_POST['update_metadata']) and continue to read).
It essentially sets $data['ppURL'] to the aliases (new AND old), and uses $page->update($data);
There is one important thing to consider: to add a new path, the key needs to be a string! This had me baffled for one hour.
So, the entire code to add one new alias, AND keep all the old ones will be:
$this_page_paths = $in_pg->getPagePaths();
$new_paths = array();
foreach($this_page_paths as $path){
if (!$path['ppIsCanonical']) {
$new_paths[$path['ppID']] = $path['cPath'];
}
}
$new_paths['add_path'] = $add_alias;
$data = array();
$data['ppURL'] = $new_paths;
$in_pg->update($data);
$in_pg is the page you want to add the alias to. It is important that you check for the path being canonical, else your main page path will be overwritten (and the page will be redirecting to /). The new path ($add_alias) is added with a string (it does not matter which string you use).
We could use rescanPagePaths to do it directly, but we should not: update fires on_page_update events, for instance.
Now to the second part: modifying .htaccess to provide compatibility.
RewriteRule ^(.*)\.phtml(.*)$ $1$2 [L]
By adding this rule you will be stripping out .phtml from your links -> thus, compatibility with the following path from our example would be preserved:
/legacy_path/legacy_name.phtml
This rewriting is necessary, as Concrete does not allow dots (.) in page paths – it replaces them by an underscore.
Concrete5 errors
Wednesday, March 31st, 2010Fatal error: Class 'CollectionAttributeKey' not found in (…)/concrete/models/collection.php on line 183
This class is defined in concrete5.3.3.1\concrete\models\attribute\categories\collection.php
The problem is easily resolved by loading the appropriate model in your code:
Loader::model('attribute/categories/collection');
Concrete5 :: interacting with the database
Wednesday, March 24th, 2010The C5 API has a class Database in the core package (libraries/database.php). Alas, this is not the documentation we're looking for (we're looking for query, etc. methods, which are not present there).
Actually the methods used come directly from the included ADODB librarary. You can download a documentation here.
Usage:
//Load the Database Layer.
$db = Loader::db();
//quote the string we'll be inserting (use get_magic_quotes_gpc() as second parameter to avoid double escaping!)
$source = $db->qstr($source, get_magic_quotes_gpc());
$content = $db->qstr($content);
$sql = "insert into IdImportWizardSources (source, content) ";
$sql .= "values ($source,$content)";
if ($db->Execute($sql)===false) {
//error handling
echo 'error inserting: ' .$db->ErrorMsg();
}
Concrete5 and db.xml
Tuesday, March 23rd, 2010How does the installation of a db.xml file in the package root directory work?
I need to create tables for my Dashboard Application (Import Wizard), which will not be used by blocks – the Import Wizard does not contain any blocks.
In the Concrete5 forum, a function installDB was mentioned. It's defined in the InstallController class like this:
protected function installDB() {
$installDirectory = $this->installData['DIR_BASE_CORE'] . '/config';
$file = $installDirectory . '/db.xml';
if (!file_exists($file)) {
throw new Exception(t('Unable to locate database import file.'));
}$db = Loader::db();
$db->ensureEncoding();
$err = Package::installDB($file);}
It ensures the db.xml file exists and after some other setup steps and check finally calls Package::installDB (Package is a model):
public static function installDB($xmlFile) {
if (!file_exists($xmlFile)) {
return false;
}// currently this is just done from xml
$db = Loader::db();
// this sucks – but adodb generates errors at the beginning because it attempts
// to find a table that doesn't exist!$handler = $db->IgnoreErrors();
if ($db->getDebug() == false) {
ob_start();
}$schema = $db->getADOSChema();
$sql = $schema->ParseSchema($xmlFile);$db->IgnoreErrors($handler);
if (!$sql) {
$result->message = $db->ErrorMsg();
return $result;
}$r = $schema->ExecuteSchema();
if ($db->getDebug() == false) {
$dbLayerErrorMessage = ob_get_contents();
ob_end_clean();
}$result = new stdClass;
$result->result = false;if ($dbLayerErrorMessage != ") {
$result->message = $dbLayerErrorMessage;
return $result;
} if (!$r) {
$result->message = $db->ErrorMsg();
return $result;
}$result->result = true;
$db->CacheFlush();
return $result;}
The Code is Copyright by Concrete5 Team.
It looks like I could just drop a db.xml file in my package directory and it will be installed. But how should I name my table?
By convention, there's different prefixes:
-
atX (atAddress, atBoolean, atDateTime) = attribute types
-
btX (btForm, btFlashContent, btSearch) = block types
But there's none for helper tables, to be used just for the Dashboard wizard.
Here's a link to the AdoDB XML Schemas (AXMLS) (the language db.xml is written in). I came up with the following:
<?xml version="1.0"?>
<schema version="0.3">
<table name="IdImportWizardSources">
<field name="ID" type="I">
<key />
<unsigned />
<autoincrement />
</field>
<field name="source" type="X" />
<field name="retrieved" type="T">
<deftimestamp />
</field>
<field name="content" type="B" /></table>
</schema>
Here I define a new table named IdImportWizardSources (Id for IdeaDay), and several field values using keywords.
Notable is the retrieved field which is automatically set to the current timestamp (deftimestamp). content has a type of BLOB which should be able to store up to 4 GB with MySQL longblob type – unfortunately the amount you can store at once also depends on your MySQL max_packet_size. (Which is usually set to around 16 MB).
The table is imported and set up automatically by Concrete, without a single line o'code.