I am very curious to know how a Drupal module can be dis-integrated into multiple include files. A number of hook support to link include components, like hook_menu, hook_theme etc.
Once I planned to simplify one of my complex module that have reached to 2.3K of lines with half of its feature set. I have to roll back all those steps due to lack of knowledge about the scope of inclusion.
Help me in this regard if there is some detailed information.
What Nikit said is correct.
I will add there are some hooks that are allowed to define which files need to be loaded. Examples of such hooks are hook_theme(), and hook_menu().
A module should never unconditionally load a file it needs calling module_load_include() from outside a function.
function book_menu() {
$items['admin/content/book'] = array(
'title' => 'Books',
'description' => "Manage your site's book outlines.",
'page callback' => 'book_admin_overview',
'access arguments' => array('administer book outlines'),
'file' => 'book.admin.inc',
);
$items['admin/content/book/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/content/book/settings'] = array(
'title' => 'Settings',
'page callback' => 'drupal_get_form',
'page arguments' => array('book_admin_settings'),
'access arguments' => array('administer site configuration'),
'type' => MENU_LOCAL_TASK,
'weight' => 8,
'file' => 'book.admin.inc',
);
// …
}
function user_theme() {
return array(
'user_picture' => array(
'arguments' => array('account' => NULL),
'template' => 'user-picture',
),
'user_profile' => array(
'arguments' => array('account' => NULL),
'template' => 'user-profile',
'file' => 'user.pages.inc',
),
// …
'user_admin_perm' => array(
'arguments' => array('form' => NULL),
'file' => 'user.admin.inc',
),
// …
);
}
Using more files, is just a matter of grouping similar things together in the same file, to keep it more managed. Typical files used is
.admin.inc for all administration stuff, menu callbacks, forms etc.
.pages.inc for frontend menu callbacks.
.theme.inc for theme functions, preprocess hooks etc.
.forms.inc for non admin forms and their handlers.
This is more a coding style that anything else. So this is just to help yourself maintain the code you have written.
It's simple, just review other big modules (like cck, views, etc). Main hooks should be in module, others should be in different files - themers, admin pages, other pages, service functions and so on...
Related
So I want my module to make a page first. But the link to the simple page should be user entered.
I`m doing this on Drupal 7.
I made a settings form in the _menu function.
$items['admin/config/content/settings'] = array(
'title' => 'Setings',
'description' => 'A form to change settings',
'page callback' => 'drupal_get_form',
'page arguments' => array('settings_form'),
'access arguments' => array('access administration pages'),
'type' => MENU_NORMAL_ITEM,
);
Then I added this:
function settings_form($form, &$form_state){
$form['locationUrl'] = array(
'#type' => 'textfield',
'#title' => t("URL kādu vēlaties?"),
'#description' => t("Example. about"),
'#required' => true
);
return system_settings_form($form);
}
And then i added to the _menu function:
$items[variable_get('admin/config/content/settings','locationUrl')] = array( //this creates a URL that will call this form at "examples/form-example"
'title' => '', //page title
'page callback' => 'DGMap', //this is the function that will be called when the page is accessed. for a form, use drupal_get_form
'access arguments' => array('access simple page'),
);
And i add this:
function page(){
return array('#markup' => '<h1>Hello?</h1>');
}
I save it all up. Clear cache. And go to settings and save the locationUrl as for example 'about'. And then try /drupal/about it gives me page not found exception.
Could anyone please help me with this? I hope that it is understandable what im trying to make.
Thanks for help.
The end thing i want to make is so that my module could create a page with custom JavaScript in it. If anyone could link me to a tutorial that would be great for this, i would really appreciate that. If there is a way for my module to place custom created JavaScript in the page that would be fine too.
Not sure if it's a good aproach for dynamic routing but you're getting the variable wrong for your second link in hook_menu(). In fact you make a route to YourDrupal/locationUrl instead of your example YourDrupal/about.
Use this for the second menu link (with an extra check on whether or not the setting is being saved):
if (!empty($setting = variable_get('locationUrl', ''))) {
$items[$setting] = array(
'title' => '', //page title
'page callback' => 'page', // use the same name as your pagecallback
// 'access arguments' => array('access simple page'),
'access arguments' => array('access content'), // temporarily use this
);
}
You also might be in need of a check on existing routes. Not sure what happens if the user has saved your form with a locationUrl like for example "admin" giving a link to YourDrupal/admin which already exists.
I'm wondering which is the best way to display a form on a page (for example : a register form for vip users ... and not in a block but as the main content).
The user.module way in user_menu (*hook_menu*) :
$items['vip/register'] = array(
'title' => 'Create new vip account',
'page callback' => 'drupal_get_form',
'page arguments' => array('vip_register_form'),
'access callback' => 'user_register_access',
'type' => MENU_LOCAL_TASK,
);
Or by creating a theme via use_theme (*hook_theme*) (fictive) :
$items['vip/register'] = array(
'title' => 'Create new vip account',
'page callback' => 'theme',
'page arguments' => array('vip_register'),
'access callback' => 'user_register_access',
'type' => MENU_LOCAL_TASK,
);
function user_theme() {
return array(
'vip_register' => array(
)
);
}
function theme_vip_register(){
return drupal_get_form('vip_register_form');
}
I'm wondering about this for theming purpose, because a designer will do the graphic integration afterwards.
Thank you for advices.
This is not an actual answer but I'm not quite sure what is your question at first place.
Drupal customs #1: Never hack core!
As its name, theme functions are just to theme something. So you need the form built first.
$items['vip/register'] = array(
'title' => 'Create new vip account',
'page callback' => 'drupal_get_form',
'page arguments' => array('vip_register_form'),
'access callback' => 'user_register_access',
'type' => MENU_LOCAL_TASK,
);
When a user accesses example.com/vip/register page, drupal_get_form function will be called with argument vip_register_form.
So now you need to define a function to return this (vip user registration) form.
function vip_register_form($form, &$form_state){
..your FAPI stuff here.
return $form;
}
Now user who open the vip register page will see this form instead of the regular form. even password and username fields will not be available unless you add them. If you want to alter the existing form, just copy the menu hook to a new path:
$user_menu_routers = user_menu();
$items['vip/register'] = $user_menu_routers['user/register'];
Now you can alter your the form at vip/register page (which is same as normal user register page) using a form_alter hook. You can theme the form manually without affecting existing one as well.
I have a content type "activities", which has three fields:
1- Programs
2- Implementation
3- Project Stories
How can I display each field in the node in a separate tab?
Thanks!
I have found an easier way to do it using the field_group module. From "Manage Display" the fields can be added to horizontal tabs fieldgroups and then the horizontal tabs fieldgroups can be added to a horizontal tabs group. See the image for further information.
In my opinion there are two ways you can achieve this.
1) Using the hook_menu() to create the tabs for your content type.
Here you will have to write your own module and the code will look something like this
/**
* Implements hook_menu().
*/
function pages_menu() {
$items['pages'] = array(
'title' => 'Menu system examples',
'description' => 'Menu system example that returns a string.',
'page callback' => 'pages_string',
'access callback' => TRUE,
);
$items['pages/default'] = array(
'title' => 'String',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['pages/render-array'] = array(
'title' => 'Render array',
'description' => 'Menu system example using a render array.',
'page callback' => 'pages_render_array',
'access arguments' => array('access content'),
'weight' => 2,
'type' => MENU_LOCAL_TASK,
);
$items['pages/render-array/tab1'] = array(
'type' => MENU_DEFAULT_LOCAL_TASK,
'title' => 'Tab 1',
);
$items['pages/render-array/tab2'] = array(
'title' => 'Tab 2',
'description' => 'Demonstrating secondary tabs.',
'page callback' => 'pages_render_array',
'access callback' => TRUE,
'type' => MENU_LOCAL_TASK,
);
return $items;
}
You then use the call back function to do your think in each of the tabs
2) Using Css and jquery to style the content in a way that it looks like a tab.
here is a great working demo for you. http://www.99points.info/2010/08/create-sexy-animated-tabs-using-jquery-and-css/
Cheers,
Vishal
A quick search on Drupal moduels gets this:
D6 - http://drupalmodules.com/module/cck-fieldgroup-tabs
D7 - http://drupal.org/project/field_group
One other module to consider, if anyone else comes looking for an answer: http://drupal.org/project/node_subpages
[[shameless plug]]
I'm creating a module that let's your users add feeds.
So i want my code to provide tabs that can be overwritten at the theme layer.
I thought this could be done with the hook_menu:
$items['tab_add_feed'] = array(
'title' => 'Add Feed',
'page callback' => 'xml_parser_add_feed',
'access callback' => 'user_access',
'access arguments' => array('manage own feeds'),
'type' => MENU_LOCAL_TASK,
);
something like the above.
But I'm using it on the front side of the site.
How can i add tabs or links on top of my page the drupal way?
//edit
There are none tabs present at the moment, maybe i have to make them visible?
fixed it by adding add feed to the main page callback function
But this is ugly, hard coded and not theme-able. waiting for a better solution.
//edit
This is the code i am using now
function xml_parser_menu() {
$items = array();
$items['xml_parser'] = array(
'path' => 'xml_parser',
'title' => t('Feed'),
'description' => t('Add/edit feeds'),
'page callback' => 'xml_parser_manage_overview',
'access callback' => 'user_access', // get user access
'access arguments' => array('manage own feeds'), // check user if premission is set
'type' => MENU_NORMAL_ITEM,
'menu_name' => 'primary-links', // add to primary menu
);
$items['xml_parser/add_feed'] = array(
'path' => 'xml_parser/add_feed',
'title' => 'Add Feed',
'page callback' => 'xml_parser_add_feed',
'access callback' => 'user_access',
'access arguments' => array('manage own feeds'),
//'access' => user_access('maintain own subscriptions'),
'type' => MENU_LOCAL_TASK,
);
return $items;
}
I think the name of the item would be something like 'user/%/add_feed', with the % argument being the user id. Also, the access callback is spelled incorrectly, should be user_access. This should create a tab for a user on the user profile page. You could also do node/%/add_feed to add a tab to a node.
While developing this module, you may find it useful to also use this:
function mymodule_init() {
cache_clear_all();
menu_router_build();
}
Until you get the menu straight.
If you want to just add an arbitrary tab to a page to add a feed, it would probably be an autonomous menu or a themed link. I would need to know more about the context of the feeds you are trying to provide and how users are subscribing.
You should be able to add a tab to the front page. Remember that the front page of your website is not really "/", it is "/node" by default (unless you changed it).
I'm just guessing because I haven't tried this, but your code should work if you change the key for your tab in $items to node/tab_add_feed.
If you have something else as your front page (ie. a view or panel), this would not apply.
Is it possible to declare and manage several custom content types inside one module? I'm creating a site that needs four custom content types and I'd like to manage them from one module instead of creating module for every content type. After some testing, I found out that it seems impossible. Because, unless hook_form and content type share the same name of module, drupal doesn't call hook_form.
Here's how I'd like to do -
function mycontent_node_info(){
return array(
'mycontent1' => array(
'name' => t('....'),
'module' => 'mycontent',
'description' => t('...),
'has_title' => TRUE,
'title_label' => t('Title'),
'has_body' => TRUE,
'body_label' => t('content body'),
),
'mycontent2' => array(
.......
),
'mycontent3' => array(
......
),
'mycontent4' => array(
......
),
);
}
function mycontent1_form(&$node){
$form['control1'] = array(
'#type' => 'select',
'#options' => array(
'0' => t('selection 1'),
'1' => t('selection 2'),
),
'#attributes' => array('id'=>'control1'),
);
$form['control2'] = array(
'#type' => 'select',
'#options' => array(
'0' => t('1'),
'1' => t('2'),
'2' => t('3'),
'3' => t('4'),
),
'#attributes' => array('id'=>'control2'),
);
return $form;
}
function mycontent2_form(&$node){
....
}
function mycontent3_form(&$node){
....
}
function mycontent4_form(&$node){
....
}
Am I doing something wrong here or is not possible and there's no alternative other than creating module for every content types. I appreciate much your help.
The prefix for all your hooks should be the name of your module, i.e. mycontent_node_info() and mycontent_form(&$node). I think the content type itself can be called whatever you want but by convention anything global that you define in a module should be prefixed with the name of the module to avoid namespace problems. So your content becomes mycontent_type1, mycontent_type2, etc... As for dealing with hook_form, I guess the way to do it is to check the type of the node passed in and act accordingly.
You might try using the Features module (http://drupal.org/project/features) to export your content types. It auto generates the code to make this work, and you can have a look at what is going wrong with your code.