How to register a wildcard url in Drupal with the hook_menu? - drupal

I'm starting to work with Drupal and I'm really confused as how to create a hook_menu function that allows you to register a URL with 1 or 2 different values, that can be hidden and not displayed in the breadcrumbs.
Any help on this would be much appreciated. Even an example.

Not sure about the breadcrumbs bit, but I think you're looking for wildcard (%) and auto-loader wildcard (%mymodule_entity) components in the path.
From the hook_menu() page...
Wildcards within paths also work with integer substitution. For example, your module could register path 'my-module/%/edit'. When path 'my-module/foo/edit' is requested, integer 1 will be replaced with 'foo' and passed to the callback function. Note that wildcards may not be used as the first component.
$items['my-module/%/edit'] = array(
'page callback' => 'mymodule_abc_edit',
'page arguments' => array(1),
);
Registered paths may also contain special "auto-loader" wildcard components in the form of '%mymodule_abc', where the '%' part means that this path component is a wildcard, and the 'mymodule_abc' part defines the prefix for a load function, which here would be named mymodule_abc_load().
$items['my-module/%mymodule_abc/edit'] = array(
'page callback' => 'mymodule_abc_edit',
'page arguments' => array(1),
);

Related

Redirect to url from drupal field

Does anyone know what the best way would be solve this:
I've got a custom content type that you can view i.e via
http://site.com/node/8
The custom content type has a field with a url (Example www.google.com)
What I'm trying to do is to create a mechanism that will automatically redirect the browser to www.google.com (the field property) when I enter a url like
http://site.com/node/8/go
I tried using the Path and AutoPath modules, but couldn't get a redirect working
The shortest implementation:
<?php
function YOURMODULE_menu() {
$items = array();
$items['node/%node/go'] = array(
'page callback' => 'YOURMODULE_redirect',
'page arguments' => array(1),
'access arguments' => array('access content'),
);
return $items;
}
function YOURMODULE_redirect($node) {
if ($node->type == 'YOUR_TYPE' && isset($node->field_YOURFIELD[0]['value']) && $node->field_YOURFIELD[0]['value']) {
drupal_goto($node->field_YOURFIELD[0]['value']);
}
}
Edit: It would be advised to add some validation and probablty other stuff, but I believe this is quite obvious. And also, depends on details of your implementation.
You can use field_redirection module .
You can use Rabbit hole and Token modules. With Rabbit hole you will be able to set a Page Redirect for the content type, and with Token you can choose a field where users will be redirected when they try to access to a node.

Drupal form validation functions

Is there anyway say Drupal to validate form elements like email fields, passwords, numeric fields validate automatically lets say bind a system validator
$form['email] = array(
'#title' => t('Email'),
'#type' => 'textfield',
'#validate_as' => array('email', ...),
...
);
To validate a numeric field in Drupal use:
'#element_validate' => array('element_validate_number')
No need to create a custom validation function.
http://api.drupal.org/api/drupal/includes%21form.inc/function/element_validate_number/7
Rimian is both correct and wrong.
The good thing as Rimian points out, is that you can attach any validation function to your form fields using the #element_validate.
However I'm not aware of a set of form api validation functions you can call to test most common things like, if the value is:
a int
a positive int
a valid date (such a function exists in the date module though)
a email-address (you can use valid_email_address to check the email, but you need a function to raise validation error)
So while you can do this, it's a bit more work than you were hoping for, as you will need to create these validation functions yourself. But once you have done this, you can reuse them with #element_validate.
The use of #element_validate is mostly centered around complex validation fx date validation, location validation and such, as it requires some work to create these validation functions. Most of the time, you don't need to validate that many numbers etc (which you quite easily could do within a normal validation function using a loop). So I'm not sure how much this will be of help to you, but it's definitely a possibility.
The Form API Validation module does exactly what you request: http://drupal.org/project/fapi_validation
For client-side validation there is also http://drupal.org/project/clientside_validation (which can use rules provided by Form API Validation).
Yep!
Though I have not experimented with it much.
http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/6#element_validate
$form = array(
'#type' => 'fieldset',
'#title' => t('Input format'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#weight' => $weight,
'#element_validate' => array('filter_form_validate'),
);

hook_menu() - an unexpected behaviour (longer path issue)

I am initializing a number of items via hook_menu (Drupal 6)
...
$items['webtv/block/%/playlist/edit/%'] = array(
...
'page arguments' => array('webtv_playlist_form', 2, 5),
...
);
$items['webtv/block/%/playlist/edit/%/filter/new'] = array(
...
'page arguments' => array('webtv_playlist_param_form', 2, 5),
...
);
$items['webtv/block/%/playlist/edit/%/filter/%'] = array(
...
'page arguments' => array('webtv_playlist_param_form', 2, 5, 7),
...
);
return $items;
First entry is a parent entry and works fine. The following two are child entries. These last two menu entries remain invalid and redirects to parent page view. I fixed it with a small modification by eliminating first wild card '%/' mark from the path definitions.
Means:
$items['webtv/block/%/playlist/edit/%/filter/%']
to
$items['webtv/block/playlist/edit/%/filter/%']
and
$items['webtv/block/%/playlist/edit/%/filter/new']
to
$items['webtv/block/playlist/edit/%/filter/new']
Please help me out what I am doing wrong by adding a wild card? Is more than two wild card are invalid?
It is not mentioned sufficiently in the documentation, but there is a limit on the number of path elements you can use for a Drupal menu callback - see the MENU_MAX_PARTS constant.
For Drupal 6, this limit is seven, which your second and third path exceeded. Both of your fixes bring the element count down to seven, which is why those work.
I have fixed the issue without excepting first wild card as I mentioned it. But I could not found any logical reason.
$items['webtv/block/%/playlist/edit/%/filter/%']
to
$items['webtv/block/%/playlist/edit/%/%']
and
$items['webtv/block/%/playlist/edit/%/filter/new']
to
$items['webtv/block/%/playlist/edit/%/new']

How do I persist form data across an "access denied" page in Drupal?

We're building a small sub-site that, on the front page, has a one input box form that users can submit. From there, they're taken to a page with a few more associated form fields (add more details, tag it, etc.) with the first main form field already filled in. This works splendidly, thus far.
The problem comes for users that are not logged in. When they submit that first form, they're taken to a (LoginToboggan based) login page that allows them to login. After they login, they redirect to the second form page, but the first main form field isn't filled in -- in other words, the form data didn't persist.
How can we store that data and have it persist across the access denied page?
You could save the data in a cookie that would be passed to the page after the login page.
Assuming that you are using the Form API to create the form, and that you have a field called "fieldname" in the function to generate the form:
function my_form() {
$form['fieldname'] = array (
'#type' => 'textfield',
'#title' => 'Some fieldname'
);
// Second field if you need to save another
$form['fieldname2'] = array (
'#type' => 'textfield',
'#title' => 'Some other fieldname'
);
}
Set the cookie(s) in the submit handler for your form:
function my_form_submit($form, &$form_state) {
// Set the cookie with their data before they are redirected to login
// Use the array syntax if you have one than one related cookie to save,
// otherwise just use setcookie("fieldname",...
setcookie("saved_data[fieldname]", $form_state['values']['fieldname']);
// Your other code
}
After they login and are redirected to page two of your form, you can read the value of the cookie(s) and if they exist, insert them into the default values of the fields:
function my_form() {
if (isset($_COOKIE['saved_data[fieldname]'])) {
$default_fieldname = $_COOKIE['saved_data[fieldname]'];
}
else {
$default_fieldname = '';
}
// Same check for the second field
if (isset($_COOKIE['saved_data[fieldname2]']))...
$form['fieldname'] = array (
'#type' => 'textfield',
'#title' => 'Some fieldname',
'#default_value' => $default_fieldname
);
// Second field if used
$form['fieldname2'] = array (
'#type' => 'textfield',
'#title' => 'Some other fieldname',
'#default_value' => $default_fieldname2
);
}
The cookies(s) will be presented on each page load after they are set, so it doesn't matter if there are several failed login attempts or other page views in between. You should probably delete the cookies after they're used or set a short expiration time so that they are cleaned up automatically.
I don't know if there is a good way to transfer form data from two entirely different forms in Drupal, especially in the context of access denied. The best choices I can find would be to:
Use the url to send the data for the field.
You could also save the data in the db, but it might be a bit tricky to extract the data again.

What is a user category exactly?

I wanted to add a tab to the user edit page ie user/%/edit/foo, and was using the twitter module as a model. After much spelunking and stepping through with a debugger, I realised that I needed to add a hook_user function in my module so that the %user_category part of the menu router path would work.
It's now functioning as expected, but I don't really have a solid idea of what I just did, and haven't found a good explanation anywhere.
Can anyone explain to me what it's about?
user_category_loads fails if given a category that does not exist, which happens at user/uid/edit/not_a_category, and it passes that category for access checks to user/uid/edit/is_a_category and thus access to those is set to false, so bam, wonky menu :'(.
When you use %user_category it means that user_category_load function is called with that argument (the uid).
The function is defined in the user module. These functions serve as a validation, if False it return FALSE, it will result in a 404, but if it return something else, like a user object, that will be passed to whatever callback function / form is run for that url.
In your case, you could probably have used %user instead which would have called user_load which is more simple, and you wouldn't have needed to do all the extra stuff to make user_category_load pass.
Summary
So user_category_load does two things.
Check that the category exist so you just can't do user/%/edit/foo.
Caches the user object.
After much trial and error, I was able to get a page working as a child of the user/%/edit path using code like this:
<?php
/**
* Implementation of hook_menu().
*/
function noc_profile_privacy_menu() {
return array(
'user/%user_category/edit/privacy' => array(
'title' => 'Portfolio privacy',
'page callback' => 'drupal_get_form',
'page arguments' => array('noc_profile_privacy_form', 1),
'access callback' => 'content_profile_page_access',
'access arguments' => array('profile', 1),
'type' => MENU_LOCAL_TASK,
'load arguments' => array('%map', '%index'),
),
);
}
/**
* Implementation of hook_user().
*/
function noc_profile_privacy_user($op, &$edit, &$account, $category = NULL) {
if ($op === 'categories') {
return array(array(
'name' => 'privacy',
'title' => t('Profile privacy'),
'weight' => 0,
));
}
}
Note that the 'name' key of what I'm returning in hook_user() is the same as what comes after user/%user/category/edit in my hook_menu() definition. I believe that is key. You will also get an error if you omit the 'load arguments' item, with exactly that value.
So I believe what the user category is is the 'privacy' in my case - the bit after edit in the path of the menu item definition.
Is this an unnecessary complication? Yes, so it seems.
Edit: I see my co-worker hefox beat me to replying to this question. I wouldn't have been able to figure all of this out without Fox's help, so mad props to the Fox.

Resources