My name is John and I'm a core hacker.
But I believe myself to be among friends and I'm sure you won't shout at me too much. I've decided to rid myself of this filthy habit. :-)
Here are the ways I've hacked core and I would like suggestions as to how to achieve this without hacking core.
1. the big one. I've altered user.module, specifically function user_validate_name. Default has a type of blacklist of characters that aren't allowed when users sign up but it's easily got round and IMHO, a bit of a mess, so last year, someone on here rewrote it for me and it's now whitelist-based, only allowing alphanumeric and hyphens.
How would I avoid hacking core to do this? Would I need a separate mini-module and how would it override user.module for this part?
2. CSS. I've changed comment.css, forum.css, admin.css, system.css. How would I do all of this in my theme's css and have it successfully override the default css files in these cases?
3. I've hacked contact.module, adding these two lines:
if( !isset( $_POST['subject']) && !empty($_GET['subject'])) // FIRST ADDITIONAL LINE
$form['subject']['#value'] = check_plain($_GET['subject']); // SECOND AND LAST ADDITIONAL LINEto allow somebody to click a link on my page and it goes to the contact form with the subject field pre-filled. Again, how would I do this without hacking core?
well, looking back on that, it's not too bad...two big bad hacks and a few CSS file ones.

The css one is easy. A: look
The css one is easy.
A: look at the element you are trying to style with firebug. That will tell you the ACTUAL css selector that is being used to add the unwanted core style.
- Add a line in your own css that uses the same syntax or is more specific in the CSS cascade, with your own attributes, and it will take priority.
EG, if you find
tbody { system.css?U (line 29)border-top:1px solid #CCCCCC;
}
to be annoying (I sure do) then your theme can go:
tbody {border-top:none;
}
DONE.
B: Alternatively, you can edit your themename.info file and add the line
stylesheets[all][] = system.csswhich will either disable system.css entirely (for a clean slate) if you don't have your own system.css in your own theme
or use your own theme system.css if you DO make one. Copy your hacked version over there.
:-)
I'm not sure about the others, but usually I trace the code around where it LOOKS like I need a hack, and find a hook call or a theme call I can hijack. More recently a drupal_alter call also indicates a hackable point.
Your contact_module one looks like it can be done fine with a hook_form_alter. The first one too for that matter.
So yeah, a mini-module.
I usually have a 'tweaks' module I throw that stuff into.
.dan.
I'm using D5 so I don't have
I'm using D5 so I don't have theme .info files.
On the CSS question, does my theme's style.css always take priority over a module's CSS file? If so, as you suggest, I can just paste all the module CSS file changes straight into my theme CSS file, can't I?
yep
Yes, your theme is deliberately loaded last - which means your rules with the same priority will always take precedece.
if you use the same rule or better according to CSS rules
.dan.
I'm so Proud of You
If we had a 12 step program for Core Hackers, you have done the hardest part.
Taken the first step and admitted you have a problem!
I don't have any hacker answers for you, but we'll get you some help to keep your code base clean.
bekasu
Solution for #3
Drupal provides hook_form_alter to allow you to muck with forms before they are displayed. To use it, you'll need to write your own custom module. Documentation can be found here: http://drupal.org/node/82920. The important part of your module will goe something like this:
<?phpfunction mymodule_form_alter($form_id, $form) {
if ('contact_mail_user' == $form_id) {
if( !isset( $_POST['subject']) && !empty($_GET['subject'])) {
$form['subject']['#value'] = check_plain($_GET['subject']);
}
}
return $form;
}
?>
I just wrote this off the cuff, so it might not work as is, but it's close.
Solution for #1
This is a little trickier, 'cause I'm not sure what your changes look like exactly, but the solution is very similar to #3. You want to replace the default form validator with your own custom function. There is a good blog post describing this here: http://symbiotix.net/articles/how-add-one-or-more-custom-submit-handlers... Basically, you want to either replace the original submit handler, or add another to run as well. Your new module that you just created to handle the contact form would be a good place to put your new submit validator function too!
The approach I'd take
I'd create a custom module to make some of these tweaks
The user hook is triggered when validating a user:
http://api.drupal.org/api/function/hook_user/5
At first glance it looks like your custom module will be able to run its own validation tests.
eg a starting point: (this is completely untested and probably has a bug or two in it)
<?php
// Note: the function names get renamed to suit your module name
function modulename_user($op, &$edit, &$account, $category = NULL) {
if ($op == 'validate' && $category == 'account') {
if ($error = modulename_validate_name($edit['name'])) {
form_set_error('name', $error);
}
}
}
function modulename_validate_name($name) {
// This is your own version of the validation function...
// it only returns something (an error message) if there's a problem
}
?>
Note: this approach will still require that the user name passes the original criteria as well.
This sounds like a job for for hook_form_alter() in your custom module.
<?php
function modulename_form_alter($form_id, &$form) {
if ($form_id == 'contact_mail_page') { // you'll need to check that this form_id is correct
// maybe this should come from the $form array - I dunno
$subject = $_GET['subject'];
// add you own check for setting the subject
// I'm not quite sure where the original values is supposed to come from so I left that bit out
if ( /* do some checking here */ ) {
$form['subject']['#default_value'] = check_plain($subject);
}
}
}
?>
Note: I think your original code was incorrect when it used #value instead of #default_value for a text field
--
Anton
I've already sorted out all
I've already sorted out all the CSS ones, so that's a big step.
On the user.module one, the original function user_validate_name goes like this:
/*** Verify the syntax of the given name.
*/
function user_validate_name($name) {
if (!strlen($name)) return t('You must enter a username.');
if (substr($name, 0, 1) == ' ') return t('The username cannot begin with a space.');
if (substr($name, -1) == ' ') return t('The username cannot end with a space.');
if (strpos($name, ' ') !== FALSE) return t('The username cannot contain multiple spaces in a row.');
if (ereg("[^\x80-\xF7 [:alnum:]@_.-]", $name)) return t('The username contains an illegal character.');
if (preg_match('/[\x{80}-\x{A0}'. // Non-printable ISO-8859-1 + NBSP
'\x{AD}'. // Soft-hyphen
'\x{2000}-\x{200F}'. // Various space characters
'\x{2028}-\x{202F}'. // Bidirectional text overrides
'\x{205F}-\x{206F}'. // Various text hinting characters
'\x{FEFF}'. // Byte order mark
'\x{FF01}-\x{FF60}'. // Full-width latin
'\x{FFF9}-\x{FFFD}'. // Replacement characters
'\x{0}]/u', // NULL byte
$name)) {
return t('The username contains an illegal character.');
}
if (strpos($name, '@') !== FALSE && !eregi('@([0-9a-z](-?[0-9a-z])*.)+[a-z]{2}([zmuvtg]|fo|me)?$', $name)) return t('The username is not a valid authentication ID.');
if (strlen($name) > USERNAME_MAX_LENGTH) return t('The username %name is too long: it must be %max characters or less.', array('%name' => $name, '%max' => USERNAME_MAX_LENGTH));
}
my altered one is
/*** Verify the syntax of the given name.
*/
function user_validate_name($name) {
if (!strlen($name)) return t('You must enter a username.');
if (strpos($name, ' ') !== FALSE) return t('The username cannot contain
spaces.');
if (!preg_match('/^[a-zA-Z0-9]+$/', $name)) {
return t('The username must be between 3-14 characters long. It may contain only letters and numbers.');
}
if (strpos($name, '@') !== FALSE &&
!eregi('@([0-9a-z](-?[0-9a-z])*.)+[a-z]{2}([zmuvtg]|fo|me)?$', $name))
return t('The username is not a valid authentication ID.');
if (strlen($name) > USERNAME_MAX_LENGTH) return t('The username %name is
too long: it must be %max characters or less.', array('%name' => $name,
'%max' => USERNAME_MAX_LENGTH));
}
the problem with the original is that I've managed to bypass their blacklist checks with a whole host of weird non-alphanumeric characters and I didn't want spaces allowed either, so I got the new function written by someone last year and it works great. Just wondering how hard it is to get into a separate module.
thanks for the replies so far. Looks like I'll have a busy weekend.
A module at it's simplest
is basically just a subdirectory called modulename, a modulename.info file, and a modulename.module file.
.info file format:
http://drupal.org/node/101009
And the .module file is just a php file containing the functions you need. Generally all the functions should start with the module name to avoid naming conflicts.
Note: the validation function you are using contains a lot of duplicate stuff from the existing function. Now that you are using a module for this, the duplication isn't needed any more - both sets of checks will be run.
Example modulename.module :
<?php
// Note: the function names get renamed to suit your module name
function modulename_user($op, &$edit, &$account, $category = NULL) {
if ($op == 'validate' && $category == 'account') {
if ($error = modulename_validate_name($edit['name'])) {
form_set_error('name', $error);
}
}
}
function modulename_validate_name($name) {
// This is your own version of the validation function...
// it only returns something (an error message) if there's a problem
if (!preg_match('/^[a-zA-Z0-9]+$/', $name)) {
return t('The username may contain only letters and numbers.');
}
}
That's all it should need.
--
Anton