RSS
 

Archive for September, 2010

Subversion and Git

30 Sep

They usually don’t play nice with each other. But what if we need a plugin/repository available for git AND for svn?

The “new” way

It used to work – but since a few weeks it doesn’t anymore. It WAS possible to checkout any github rep via SVN. Sounds crazy, but that was really working: https://svn.github.com/[user]/[repository] Anyway – doesn’t seem to be to stable, yet. So I don’t use this approach anymore.

The classic way

This works no matter what Subversion or Git do. It relies on the fact that they don’t get mixed up. We exclude the .git and .svn dirs from each others rep.

Let’s say we already have our svn rep. We also want it to be in git. We first create an empty git rep in the root folder of the rep: “Git Create Rep here”.

I assume you already have the following excludes in your global svn settings (called “global ignore pattern”): _* .git .gitignore As you can see, i also like to exclude all tmp files (starting with _ like “_test.php”).

Create a .svnignore file in the root folder of the rep:

.svn
_*
We want to exclude tmp files as well as all svn related stuff.

Now add your files to git “master”, commit them and synch with your newly created git rep. Usually the url is something like: git@github.com:[user]/[repository].git

That’s it – your done! They don’t interfere with each other. So there shouldn’t be any complications. Furthermore you can now chose which merging “engine” you want to use as they both work on the very same rep.

Live Example

Math Plugin: It is located in subversion (rep is not public, though) and in git. If I update the code, I just have to make sure, that i commit to both “sources”. Otherwise one will be outdated. Just to be sure you should consider ONE source the primary one. No matter what this should be the one to commit to, first.

 
No Comments

Posted in Git, SVN

 

Useful Windows 7 Tricks

28 Sep

We all agree that Windows 7 is the most sophisticated version so far. WinXP is totally outdated, ME and Vista were crap. But despite the more stable workstation and some really good new features windows still lacks some basic stuff or at least kept its annoyances :) I will also point out some useful tricks and tips.

No forced restart after updates

The most annoying thing still remains the (forced) restart after updates all the time. … English: Deactivate German: Deaktivieren

Tips and Tricks

Using 2 monitors you can easily send active windows from one to the other by using the combination [WINDOWS]+[SHIFT]+[ARROW left/right]. With [WINDOWS]+[SHIFT]+[ARROW up/down] on the other hand you can minimize/maximize the current window.

For opening programs with more rights simple press [STRG]+[SHIFT] while clicking on it.

Minor probs

The thumbnail previews of the super bar are usually coming up really slow. Change to “HKEY_CURRENT_USER\Control Panel\Mouse” and lower the value from “400″ to “0″ or anything in between.

Missing tips?

Tell me and I will add it!

 
No Comments

Posted in Common

 

Setting up PHPDesigner for CakePHP

22 Sep

There are many great IDEs for PHP development. One is PHPDesigner, which is mainly for windows.

With some minor adjustments it works pretty well with cake.

Preferences

Go to: Tools -> Preferences [CTRL + E]

  • General File Encoding: UTF8 Trim on save: Trim Right (no need to save empty spaces to the right) Detect Changes: Yes (very useful!) Sublink -> Document Types: Add *.ctp to PHP
  • Editor Tabs: YES (we want to use one tab as indend) Tabs to spaces: NO (should not be converted) Tab Width: 2 (so one tab will be 2 spaces wide)
  • Debugger Syntax Check: I like to use a little bit different color here (a brighter red).

Those are just the most important ones. You can play with other settings, as well.

Project Setup

Go to: Projects -> Project Manager [F11]

New => Add “Title” => Select “Root Folder” (should be your app directory!) => Add “Libraries” (I usually add the cake folder, but you could also add the vendors folder here) => Just hit “Next” until it’s finished. You can now open the project. After a few seconds/minutes all functions are indexed and can be looked up with “Rightclick -> Go to declaration”.

All PHP-internal functions can be looked up with F1 (Quick lookup in the PHP Manual).

To read more about code completion, see my other article about it. It describes how you can use a shell script to dump all components, helpers, … into one “dummy” file to help PHPDesigner index and auto-complete all library functions.

Here is one example of how all cake and app functions get auto-completed and displayed:

Final Tips

Sometimes the files you open are still ANSI. If you add UTF8-Signs save it as UTF8 in order to display them correctly. Otherwise it will either be nonsense or nothing at all. After editing or creating Projects you need to properly close and restart your editor. Otherwise changes might be lost after the next restart. This is some bug of PHPEditor, I guess.

PHPDesigner and Linux

The best of all IDEs is nativly only available for Windows. For all those poor rats out there that use Linux: www.zephid.dk/2008/07/01/using-phpdesigner-under-linux/ Seems to work (if you believe the comments).

 
No Comments

Posted in PHP

 

Saving Model Data and Security

21 Sep

In google groups there are quite a few discussions every month about security against (primary key) injection, xss or other things. And yes, the default templates will not protect you from any of this. They are meant to produce quick “standard” output. But they are not prepared for the real world, at all. It is actually quite irresponsible not to use custom templates or manually adjust the forms/views. UPDATE: I could finally convince them to use h() in their default templates! A big WIN for security!

A while ago i wrote about how to create custom templates. See this article for details. You will also notice, that the controller actions and views are protected against basic – and only basic – injections. That is mainly xss-attacks. By using h() in the views we can make sure that dangerous strings get destroyed. The custom templates also ensure, that the record we edit/delete exists.

What it still doesn’t do is protecting the model data.

Note: some of the following could be achieved by adding the security component. But sometimes, it is not possible or not desired.

Protection against too many fields

Another tricky one. Again, Firebug can be used to violate your forms. Lets say, a user wants to have more rights – and he happens to know that User.role_id must be 2 instead of 1 in order to be “admin”. In the User edit form (besides “email”, “birthday” and stuff) he could then inject a form input called “role_id” which usually is not there. With a normal $this->save() operation this would actually work! After the next login his role would be 2 (= admin). What can we do about something like that?

Cake has a “fieldList” as third parameter of the save() function build in:

if ($this->User->save($this->data, true, array('id', 'gender', 'birthday'))) { # note the id (needed here)
Id, gender and birthday are passed, any other field in $this->data will be omitted (especially our role_id^^). I use this in all sensitive forms (user and role related).

Careful: If you don’t pass the id, it will be omitted and the model cannot set its primary key (and might perform “add” instead of “edit”!

Primary key injection on edit actions

This is the not quite the same as the above topics. All edit actions have the primary key in the forms. I usually remove them, but sometimes I am too lazy to do that (because you need to alter the form url then as well). So the id actually exists – but won’t be checked prior to the save. Well, here are some custom solutions – there are more general ones, of course. But I just want to point out the problem and a way to fix it. These could be added to your bake templates, for instance.

Lets take our custom template from above (see link) and modify it:

function edit($id = null) {
    if (empty($id) || !($data = $this->User->read(null, $id))) {
        //error flash message
        $this->redirect(array('action' => 'index'));
    }
 
    if (!empty($this->data)) {
        # solution 1: just override with the "correct" one:
     $this->data['User']['id'] = $id;
 
        # solution 2: check against "correct" one
     if ($this->data['User']['id'] != $id) {
            //BLACKHOLE
        }
 
        if ($this->User->save($this->data)) {
            //OK
        } else {
            //ERROR
        }
    }
}
Solution 1 is self-explanatory and cannot go wrong. Solution 2: After we made sure the record exists, we check the passed id against the record id. If they don’t match someone tries to trick us. Otherwise the record can be saved. Solution 3: Remove the id from the passed arguments (third parameter of save() – but makes it more complicated) Solution 4: Put the check in beforeValidate() of your app_model.php. Usually – with the above template you have just “read” the record, so the model has the id in $this->id. It could then return false if the passed id and $this->id do not match. Thats actually my favorite. But I am not sure if it works in all cases. You may only check if you are actually saving a record – meaning: both ids have to be not empty. Only then it makes sense to apply this specific piece of validation.

Protection against missing fields

This is a tricky one. Most beginners (I didn’t either) dont realize that a form can be modified with tools like and Firebug (Firefox Addon) and that Cake would not bother. Lets say we have a registration form and your gender has to be provided. But you don’t want to. So you simply remove this input from the form and submit it. ViolĂ  – it worked!

Why does it work? Cake automatically applies the validation “needed” for this form. All fields that are passed (it doesn’t matter if they are empty or not – they just have to exist on submit) will be checked. Validation rules for fields that did not get passed will be omitted. And thats our problem. Right here.

OK, we need to make sure that the form fields are actually part of the model data before the validation kicks in. I wrote a app model extension which can be used – you could also write a model function which one by one adds the required fields if the field does not exist.

Put this in your app model:

/**
 * set + guaranteeFields!
 * extends the core set function (only using data!!!)
 * 2010-03-11 ms
 */
function set($data, $data2 = null, $requiredFields = array()) {
    if (!empty($requiredFields)) {
        $data = $this->guaranteeFields($requiredFields, $data);
    }
    return parent::set($data, $data2);
}
 
/**
 * make sure required fields exists - in order to properly validate them
 * @param array: field1, field2 - or field1, Model2.field1 etc
 * @param array: data (optional, otherwise the array with the required fields will be returned)
 * @return array
 * 2010-03-11 ms
 */
function guaranteeFields($requiredFields, $data = null) {
    $guaranteedFields = array();
    foreach ($requiredFields as $column) {
        if (strpos($column, '.') !== false) {
            list($model, $column) = explode('.', $column, 2);
        } else {
            $model = $this->alias;
        }
        $guaranteedFields[$model][$column] = ''; # now field exists in any case!
 }
    if ($data === null) {
        return $guaranteedFields;
    }
    if (!empty($guaranteedFields)) {
        $data = Set::merge($guaranteedFields, $data);
    }
    return $data;
}
We can now make sure removing any form field will have no effect :) I call it “enforced whitelisting”.

Case 1: Using $this->Model->set($this->data) and $this->Model->save() – Important: note the NULL in save(), we may not pass the data a second time!

$this->User->set($this->data, null, array('gender', 'birthday', ...));
$this->User->save();

Case 2: Using directly $this->Model->guaranteeFields($fields, $this->data) and $this->Model->save($this->data)

$data = $this->User->guaranteeFields(array('gender', 'birthday', ...), $this->data);
$this->User->save($data);

Lets combine them

$this->data['User']['id'] = $id; // on edit
$this->User->set($this->data, null, array('gender', 'birthday', ...));
if ($this->User->save(null, true, array('id', 'gender', 'birthday', ...))) { # note the id (needed here)
 //OK
} else {
    //ERROR
}
Now thats protection :) Only the fields that you want to are passed and all the fields that should be there are there. And the primary key is the one it is supposed to be. If your validation rules are correct, you got the perfect form handling, I guess.

Blacklisting

// put this in your app_model.php
function blacklist($blackList = array()) {
    return array_diff(array_keys($this->schema()), $blackList);
}
Although not recommended for general usage, this might be useful for some rare occasions. It returns all fields of the current database table except the ones you blacklisted.

Usage:

$this->User->save(null, true, $this->blacklist(array('birthday'));
If you want to pass non-existing fields to the model for validation you need to array_merge() them!
$this->User->save(null, true, array_merge(array('virtual_field'), $this->blacklist(array('birthday')));

Server-side and Client-side Validation

First one is what we just discussed. Client-side usually is JS or browser-specific validation. You should NEVER rely on client-side validation – it can easily be tricked. It is nice to have it – “additionally” in order to prevent unnecessary posts, but the real deal should be what your model validation returns.

SQL Injection

I always smile about other webprojects that are vulnerable to this attack. Its just hilarious. Thats why you use a framework. Because it takes care of the basic problems. And this is one of it. There are only very few things to be aware of: Always manually escape manual SQL queries! CakePHP only takes care of the queries run by $this->find() etc. So if you ever have to write your own SQL script, you will need to do that yourself.

The myth about “required” and “allowEmpty”

They seem to be the same, don’t they? But there is a huge difference between them (the cookbook does mention it here). “required” is like my “Protection against missing fields”, only hard-coded. These fields will validate false, if this field is not present on validation. It does not matter if the field is empty or not. The word “required” only applies to the database field, not its content. “allowEmpty” is equal to “minLength 1″ and ONLY validates this field if the field has been passed to the model. If not, it will not return false. You cannot make sure that a specific field is always filled out with this parameter alone.

If you want to make sure, that the field cannot be avoided AND has to be filled out, you need to combine both rules:

var $validate = array(
    'status' => array(
        'maxLength' => array( # this can be any label you want
         'required' => true, # careful!
         'allowEmpty' => false,
            'rule' => array('maxLength', 5),
            'message' => array('valErrMaxCharacters %s', 5), # a modification of mine to allow better i18n support
         'last' => true # important for more than one rule per field!!!
     ),
        ... # other rules
 ),
    ... # other fields
);
The “careful” stands for: this can get ugly really fast. I never use “required” hard-coded. As soon as you use ajax toggling of some fields or forms which only change a small part of the record, you will get validation errors because the required fields are not present (although you don’t even want to change them). You would need to unset() those rules manually. See the above “Protection against missing fields” for a cleaner approach.

Anyway, i hope this clears up things a little bit.

If you really feel that you need to use “required” you should only use it combined with ‘on’ => ‘create’, as well. In 99% of all cases you end up with never validating “edit” validations otherwise. That’t because you often save only a part of the data where you normally wouldn’t provide those required fields. So as a guideline: - Either don’t use required attribute at all and use my whitelisting approach from above - Use required with “on create” IF (!) the field always has to be present for all create actions. For other actions use whitelisting and guaranteeFields

A few words to validation rules in general

You can add rules for “imaginary fields”, as well. The fields don’t have to exist in the database model. This way you can easily use inputs which are validated separately and combined later on (eg: pwd and pwd_repeat resulting in the final sha1-hashed “password”). Always use “last”=>true for your rules. Unfortunately, the default value is still false here. “true” makes sure that after the first rule already returned false, the following ones are not checked as well (overhead and totally useless). Also sort your rule array properly. The rules are checked from top to bottom, so it would make sense to put all complicated stuff at the bottom and first check on simple “notEmpty” or “email” rules. The most likely ones to fail at the beginning. Why should you check “isUnique” (database query!) before “notEmpty”? Why should you check an email address exists in the database if it is invalid in the first place?

Last words

Why didn’t I use Security Component for some of the above tasks? Well, it was a bitch to use in the first months. All the time some kind of black holes (white screen of death) without any indication what went wrong. Login/Logout, some other forms as well. With the first ajax forms it got even more messy and i deactivated it at last. Works quite well without it, if you protect the forms manually. With lots of ajax stuff going on you have to do that yourself, anyway… And against certain attacks the Security Component won’t help anyway (PrimaryKey injection in edit forms etc). So at some point you will have to deal with it yourself. You have to decide to what point. But better sooner than later. If you adjust your bake templates before you start a new project it will save you lots and lots of time while having secure forms and procedures.

 
9 Comments

Posted in CakePHP

 

External links and the target attribute

20 Sep

I like external pages to open in a new window. So that my page stays open. But target attribute is deprecated. If you want to validate the XHTML pages, it would result in errors. The thought behind it was to allow the user to decide for himself if he want to open a new tab or if he want to go on in the same tab. Often times users just click on the link without thinking about it.

Sometimes as a web programmer it’s just too convenient to simply add ‘target=”_blank”‘ to external links. Quick and dirty. There is a Javascript-solution, though. The page itself is XHTML valid, but the behavior is the same as with the target attribute.

It scans for any link that starts with “http://” – in cake usually an external link (internal links are absolute to the root, like “/pages/xyz”).

(function() {
    var className = "external";
    var target = "_blank";
 
    var _onload = window.onload;
 
    window.onload = function() {
        var local = new RegExp("^" + window.location.protocol + "//" + window.location.hostname, i);
        var links = document.links;
 
        for (var i = 0; i < links.length; i++) {
            var link = links[i];
            if (/^https?:\/\//i.test(link.href) && !local.test(link.href)) {   // Not a local link.
                if (link.className && (link.className == 'internal' || link.className.split(" ")[0] == 'internal')) { // enable "override"
                    continue;
                }
                if (link.className && (link.className == 'external' || link.className.split(" ")[0] == 'external')) { // enable "override"
                    // we do not need to add the class again  
                } else {
                    link.className = className + (link.className ? " " : "") + link.className;
                }
                link.target = target;
                link.title = (link.title ? link.title+ " " : "") + "(opens new window)";   // get a title info as well 
            }
        }
 
        if (_onload) _onload();    // Play nice with others.
    }
})();
Of course you can manually override it by using 'class="internal"' for always opening it in the same window or 'class="external"' to always open in a new window.

And for all of our jQuery users ;) :

$(document).ready(function() {
    $("a[href=^http://], a[href=^https://], a.external").attr("target", "_blank");
    $("a.internal").removeAttr("target");
});

Examples:

<a href="/members">opens in the same window</a>
<a href="http://www.domain.e/some_url">opens in a new window/tab</a>
 
<a href="http://www.domain.e/some_url" class="internal">opens the same window</a>
<a href="/members" class="external">opens in a new window/tab</a>

 
1 Comment

Posted in HTML, jQuery, JS

 

Keeping track of users/guests

17 Sep

On the one hand, there is piwik and other tracking tools. But they usually only track “total users per day”. If you want to track users in real time, you can easily set up something yourself.

We need to set up a model + controller + views for “online_activities”. In the admin backend we can then track the users currently online – and where they are.

The sql table could look like:

CREATE TABLE IF NOT EXISTS `online_activities` (
  `id` int(10) unsigned NOT NULL auto_increment,
  `session_id` varchar(64) collate utf8_unicode_ci NOT NULL,
  `user_id` int(10) unsigned NOT NULL default '0',
  `created` datetime NOT NULL,
  `modified` datetime NOT NULL,
  `ip` varchar(39) collate utf8_unicode_ci NOT NULL,
  `host` varchar(128) collate utf8_unicode_ci NOT NULL,
  `last_page_url` varchar(255) collate utf8_unicode_ci NOT NULL COMMENT 'last page user has been visiting',
  `agent` varchar(255) collate utf8_unicode_ci NOT NULL COMMENT 'browser',
  `clicked_from` varchar(255) collate utf8_unicode_ci NOT NULL COMMENT 'referer',
  PRIMARY KEY  (`id`),
  KEY `user_id` (`user_id`)
);
Be aware that it can be prohibited in some countries to log ip/host etc.

You need then to create an update method in your model using session_id() as identifier. If you track logged in users as well, i would also make sure rows with several browsers (and therefore different session_ids() would not count twice. They should then have the same user_id. Similar, it is with bots (the session_id() changes all the time, the ip/host should stay the same, though).

function update($where = null, $from = null) {
    $where = '/'.$where; # from(=referer) has already one / at the beginning
 if ($where == '//') { # the homepage would just be /
     $where = '/';
    }
    ...
    $record = $this->find('first', array('conditions'=>array('session_id'=>session_id())));
    if (empty($record)) {
        # new entry
     ...
    } else {
        # update existing one
     ...
    }
}
I have a highly customized method, so its not much of use to display it here. I am sure you can come up with the missing lines of code.

Finally: This little piece of code should be in the beforeRender() method:

$this->OnlineActivity->update($this->params['url']['url'], $this->referer());

Final remarks

Don’t forget to clean the table every now and then. Depending on the traffic it can grow very very fast.

 
2 Comments

Posted in CakePHP

 

Necessary Core Hacks – Cake1.3

12 Sep

As of now there are some litte adjustments that are either necessary or at least very useful to apply

I keep this list here up to date and will remove or add hacks if something changes (usually the cake team is quite fast in applying appropriate patches).

For me, the very first and most important one for cake1.3 was this:

Disabling the old $helper syntax

Otherwise you gain nothing from the new one ($this->Helper), because the old ones are refererred to them overriding any local variable and totally messing everything up

Example: $this->Vote->check() $vote->check() both work on 1.3 (downwards comp.) BUT: If you try to use $vote in any other way (locale variable), it will screw up $this->Vote too (which is our helper). Lets say we have a controller and model called “votes” and “Vote:

// retrieve all model records
foreach ($votes as $vote) {
    // BAM - big problem! helper $vote AND helper $this->Vote are not usable anymore, neither is $vote as variable
}

The solution: getting rid of the old one altogether

Thats how it’s done – put this into your configs:

$config['Core'] = array(
'disableOldHelperSyntax' => 1
);
And change line 710 of /cake/libs/view/view.php (in the cake core!) from
${$name} =& $helper;
to
if (!Configure::read('Core.disableOldHelperSyntax')) {
    ${$name} =& $helper;
}
Any project that does have the above 1 in the “disableOldHelperSyntax” setting will not use the old syntax anymore. The core itself is not modified by it!

// inside a view
foreach ($votes as $vote) {
    echo $this->Vote->calc($vote); // everything is fine now
}

Some could argue: “just use variables that will never be used as helper”. But thats crap – maybe some day there is such a helper and your code gets screwed up. So until this backwards comp. is removed this is my answer for that problem. You are now free to use any local variable you want (well, almost anyway). Ticket: Link

Proper logging feature for debug = 0

Although this is probably the most useful debugging feature ever it didnt quite make it into the core. We all know that even for debug = 0 there are now log files of errors/warnings etc. Thats already very helpful if there are any errors occuring in live mode. But usually it just tells you THAT something went wrong, not WHERE. This little patch logs the stracktrace of this error to /app/tmp/logs/trace – very adjustable per configs settings

The new “write” function in /cake/libs/log/file_log.php looks like this:

/**
* Implements writing to log files.
*
* @param string $type The type of log you are making.
* @param string $message The message you want to log.
* @return boolean success of write.
*/
function write($type, $message) {
    $debugTypes = array('notice', 'info', 'debug');
 
    if ($type == 'error' || $type == 'warning') {
        $filename = $this->_path  . 'error.log';
    } elseif (in_array($type, $debugTypes)) {
        $filename = $this->_path . 'debug.log';
    } else {
        $filename = $this->_path . $type . '.log';
    }
    $output = date('Y-m-d H:i:s') . ' ' . ucfirst($type) . ': ' . $message . "\n";
    $log = new File($filename, true);
 
    /** CORE-MOD for ability to trace logged events - 2010-07-16 ms **/
    $debug = (int)Configure::read('debug');
    if (($debug == 0 && (Configure::read('System.trace') === true || Configure::read('System.trace.prod')) || $debug > 0 && (Configure::read('System.trace') === true || Configure::read('System.trace.dev'))) && ($stackTracing = Configure::read('System.stackTracing'))) {
        $continueTracing = true;
        $path = $this->_path . 'traces' . DS;
        if (!file_exists($path)) {
            if (!mkdir($path, 0777)) {
                // abort; raise notice?
                $continueTracing = false;
            }
        }
        if ($stackTracing !== true) { // log all events otherwise
            $stackTracing = (array)$stackTracing;
            if (!in_array($type, $stackTracing)) {
                // event to log not in list; abort
                $continueTracing = false;
            }
        }
        if ($continueTracing) {
            $trace = new File($path . $type . '_' . time() . '.log', true);
 
        if (!class_exists('CustomDebugger')) {
                require (LIBS . 'log' . DS . 'custom_debugger.php');
            }
            $CustomDebugger = new CustomDebugger();
 
            $traceOutput = '';
            $traceOutput = $CustomDebugger->trace();
            $trace->append("Message: '" . $message . "'\n" .
                "Referer: '". env('HTTP_REFERER'). "'\n" .
                "Location: '". env('REDIRECT_URL'). "'\n" .
                "Router::url(): '". Router::url(array(), true). "'\n" .
                "UID: '". (defined('UID')?UID:'---') ."'\n" . // I even log the user who caused this
                $traceOutput . "\n\n"
            , true);
            $trace->close();
        }
    }
    /** CORE-MOD end **/
 
    if ($log->writable()) {
        return $log->append($output);
    }
}
I did put the cake debugger into the same folder – slightly modified (using “txt” output). You could do the same.

Anyway, the configs could look like this:

$config['System'] = array(
    'stackTracing' => array('debug', 'warning', 'error', 'notice', 'info', '301', 'cacheprob'),
    'trace' => array('dev'=>true, 'prod'=>true),
);
It does only trace the above error types. and you can chose between development and productive environment. Still place for improvment of course, but I already discovered hundreds of small bugs that I would have never found without it. I would have known that those errors happenened, but actually finding them is usually impossible if the error message is something like “Notice (8): unserialize() [function.unserialize]: Error at offset 11676 of 11722 bytes in [H:...\cake\libs\cache\file.php, line 176]“. It is that simple: The core files trigger the error but there is no way to tell which app file actually is responsable if no trace is logged.

Ticket-status on this matter: cakephp.lighthouseapp.com/projects/42648/tickets/267

Changing default value of Configure::read() to NULL

There is no way to get all the set configure values. So this little tweak makes it possible. It doesnt make sense to use “debug” as default, anyway.

In /cake/libs/configure.php at line 154:

function read($var = null) { // changed default to NULL
    $_this =& Configure::getInstance();
 
    # fix for retrieving all configure vars (debugging purposes etc) - 2010-09-12 ms
 if ($var === null) {
        return (array)$_this;
    }
    # end fix
 ...
Without passing any parameter it will now return all set config arrays.

Misc

Some pending tickets or things i considered changing (but didn’t do it yet) are located here.

In the model.php, I don’t like “last”=>false as default value (should be true – logically):

$default = array(
    'allowEmpty' => null,
    'required' => null,
    'rule' => 'blank',
    'last' => false,
    'on' => null
);
Ticket: cakephp.lighthouseapp.com/projects/42648-cakephp/tickets/924

Form validation “required” in form helper is not exactly that what you would expect – or want. Details coming

 
2 Comments

Posted in CakePHP