Posts Tagged ‘explanation’

Concrete 5 SQL errors during installation

Friday, February 3rd, 2012

We have seen this error with Concrete 5.5.1

You try to setup Concrete 5, and get strange SQL errors during the installation:

If installing with an empty site, like this

c5-nosample-content-chosen

you get:

c5-mysql-error

mysql error: [1048: Column 'uID' cannot be null] in EXECUTE("INSERT INTO Config (cfKey,cfValue,uID) VALUES ('NEWSFLOW_LAST_VIEWED','FIRSTRUN',NULL)")

If installing with Sample content with Blog:

c5 with sample content

The error you get is:

Catchable fatal error: Argument 1 passed to ContentImporter::importPageAreas() must be an instance of Page, boolean given, called in /concrete/libraries/content/importer.php on line 75 and defined in /concrete/libraries/content/importer.php on line 197.

 

Solution:

You don't run Varnish by any chance, do you? We do, and we had it set up to … dispose of some cookies. The solution is, in Varnish' case, to enable pass-through for the host you are installing on.

You want to enable cookies.

 

Explanation:

The code which breaks is located in concrete5.5.1\concrete\models\package\starting_point.php

Config::save('SITE', SITE);
Config::save('SITE_APP_VERSION', APP_VERSION);
$u = new User();
$u->saveConfig('NEWSFLOW_LAST_VIEWED', 'FIRSTRUN');

As you see, this is the first time a User is created and used.

The User class is defined in concrete5.5.1\concrete\models\user.php

if (isset($_SESSION['uID'])) {
$this->uID = $_SESSION['uID'];
$this->uName = $_SESSION['uName'];
$this->uTimezone = $_SESSION['uTimezone'];
if (isset($_SESSION['uDefaultLanguage'])) {
$this->uDefaultLanguage = $_SESSION['uDefaultLanguage'];
}
$this->superUser = ($_SESSION['uID'] == USER_SUPER_ID) ? true : false;
} else {
$this->uID = null;
$this->uName = null;
$this->superUser = false;
$this->uDefaultLanguage = null;
$this->uTimezone = null;
}
$this->uGroups = $this->_getUserGroups();
if (!isset($args[2])) {
$_SESSION['uGroups'] = $this->uGroups;
}
}

The constructor checks in the Session for a valid user ID. If no user ID is found, it is set to zero. This happens if you throw away the cookies! Because the session is stored in them.

HTH. Feel free to comment on similar errors and solutions.

Triggering JQuery events in IFRAMEs

Wednesday, February 23rd, 2011

Apparently it is not possible to trigger JQuery events in IFRAMEs using the syntax:

$("#iframe").contents().find("#testdiv").triggerHandler('click');

The event will not be fired.

The other way around (IFRAME -> trigger events in parent) has been already discussed on the web extensively.

The solution is to not use JQuery at all, just use an ordinary function to call in your IFRAME which would then do what the handler should have been doing.

From the parent:

frames[iframe_name'].testfunction();

acts on the IFRAME

<IFRAME name="iframe_name"></IFRAME>

which in turn loads a page with

function testfunction();

Developing with Concrete: New Block Type not showing up

Wednesday, November 17th, 2010

Problem: you are adding a new block type to your package. You are updating the package using Concrete's update functionality, but no new block type shows up.

Solution: remove the package completely (uninstall – but leave it in the package directory, when C5 offers to move it to trash!), and install it again. Your block type should show up now.

Adding aliases to Concrete5 pages programmatically.

Sunday, April 4th, 2010

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

Wednesday, March 31st, 2010

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 and db.xml

Tuesday, March 23rd, 2010

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.

FormHelper select

Friday, March 19th, 2010

Not understanding the Concrete 5 API comment on the select function of the Form Helper, I looked into the source:

/**
* Renders a select field. First argument is the name of the field. Second is an associative array of key => display. Second argument is either the value of the field to be selected (and if it's blank we check post) or a misc. array of fields
* @param string $key
* @return $html
*/
public function select($key, $values, $valueOrArray = false, $miscFields = array())

Select builds a SELECT Dopdown with OPTIONS. That much I already understood from the API.

Here's my explanation of the parameters passed in to select:

  • $key: the name of the element to be created.
  • $values: an array of options, stored as $k=>$value. Used like this:
    '<option value="' . $k . '" ' . $selected . '>' . $value . '</option>';
  • $valueOrArray: this can either be set to the default value to be selected (use the option value, not the displayed text between the tags) OR it can be an array and is used as the array to populate $miscFields (see explanation there), omitting the default value. A bit unusual perhaps, probably it's a timesaver when you code these forms a lot.
  • $miscFields: An associative array ($k => $value): insert additional keys into the tag (i.e. multiple="multiple"

The fun in using select is, it STORES your settings between sessions. Boy, I should have used it earlier, when hand-coding this for other parts of the project.

Another note on ValidationErrorHelper:

/**
* Returns whether or not this error helper has more than one error registered within it.
* @return bool
*/
public function has() {
return (count($this->error) > 0);
}

As one can read from the source above, the Validation Helper returns whether there are errors (even if it is only one) or there are none!