My name is John and I'm a core hacker.

esllou - April 28, 2009 - 23:08

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 LINE

to 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

dman - April 28, 2009 - 23:54

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

which 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

esllou - April 29, 2009 - 00:20

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

dman - April 29, 2009 - 00:30

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

bekasu - April 29, 2009 - 01:52

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

imrook - April 29, 2009 - 03:42

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:

<?php
function 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

imrook - April 29, 2009 - 03:51

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

styro - April 29, 2009 - 04:20

I'd create a custom module to make some of these tweaks

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.

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.

3. I've hacked contact.module to 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?

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

esllou - April 29, 2009 - 14:50

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

styro - April 29, 2009 - 21:02

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

 
 

Drupal is a registered trademark of Dries Buytaert.