Archive for the ‘Concrete 5’ Category.

Concrete 5 site is suddenly down

The symptoms:

Your site is suddenly down. If in production mode, a blank & empty page is displayed.

The solution:

After checking that the MySQL daemon still works, Apache is running, PHP is working and the configuration has not been changed (a test.txt file is served correctly), you might want to look into your PHP error log.

Here’s how you add PHP error logging to your .htaccess, if you do not have it already:

# php error logs..
php_flag display_errors off
php_flag log_errors on
php_value track_errors on
php_value error_log /path/to/php.error.log

Be sure to chown the file to www-data:www-data or whatever your PHP process is running under.

In my case, the culprit was a very strange “duplicate entry” error:

[09-Jun-2010 20:18:49] PHP Fatal error: Uncaught exception ‘ADODB_Exception’ with message ‘mysql error: [1062: Duplicate entry '300969' for key 1] in EXECUTE(“insert into PageStatistics (cID, uID, date) values (‘1′, 0, NOW())”)
‘ in /…/concrete5.4.0.5/concrete/libraries/3rdparty/adodb/adodb-exceptions.inc.php:78
Stack trace:
#0 /…/updates/concrete5.4.0.5/concrete/libraries/3rdparty/adodb/adodb.inc.php(1037): adodb_throw(‘mysql’, ‘EXECUTE’, 1062, ‘Duplicate entry…’, ‘insert into Pag…’, false, Object(ADODB_mysql))
#1 /…/concrete5.4.0.5/concrete/libraries/3rdparty/adodb/adodb.inc.php(993): ADOConnection->_Execute(‘insert into Pag…’)
#2 /…/updates/concrete5.4.0.5/concrete/libraries/3rdparty/adodb/adodb.inc.php(761): ADOConnection->Execute(‘insert into Pag…’, Array)
#3 [internal function]: ADOConnection->Query(‘insert into Pag…’, Array)
#4 /…/updates/concrete5.4.0.5/conc in /…/updates/concrete5.4.0.5/concrete/libraries/3rdparty/adodb/adodb-exceptions.inc.php on line 78

After checking the appropriate table (used for logging visits to your C5 page), I could not understand the error – there was no entry for key 300969, the last entry was 300968.

Here’s a quick way to turn off page statistics, which will fix this error and bring your site back on: Put this line o’code in your config/site.php

define(‘STATISTICS_TRACK_PAGE_VIEWS’, false);

In PHPMyAdmin there’s the option to manually increase the auto_increment value. I increased it by one to 300970 and the site was working again, with statistics (comment the line you just inserted.)

There’s a couple of pages out there, currently having this problem (searching with Google helps). I’m offering a low-cost watchdog service for Concrete5 sitesbe back up again before your visitors notice!

Bug in Concrete 5

If a page name contains “/” (the slash), the Concrete5 edit bar does not appear on the page. (At least in Opera 10)

Solution: go to the Dashboard, edit the page name (hint: remove the slash) from the Sitemap there and visit the page again.

Concrete5 does not update correctly?

Starting 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?

My 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

If 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.

Suppose, 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

Fatal 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

The 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

How 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.

Russian and Chinese editing with Concrete 5

concrete multi language

It is not enough to add a Character set to your Template // or store it in UTF-8 without BOM (so the headers attached by PHP won’t break).

If TinyMCE only displays gibberish instead of the Russian / Chinese / … text you entered in edit mode, you should edit your .htaccess (located in the root directory of your site) and add the following:

AddDefaultCharset UTF-8

Optionally, while you’re at it, you can enable caching for your site:

#Speed UP!
<IfModule mod_expires.c>
ExpiresActive On
ExpiresDefault “access plus 10 years”
ExpiresByType text/html “access plus 30 seconds”
</IfModule>