Docs:
api:
Refer: refer: https://drupal.org/getting-started/before/overview Everyone planning and building Web solutions with Drupal benefits from understanding what a “hook” is—and why Drupal is not a CMS.
Everyone involved needs to understand that they can architect a Drupal site that offers a more-sophisticated set of features than a WordPress site, because Drupal is not a content management system (CMS); it is a content management framework.
For me now especially with Drupal 7, it is an excellent web application framework and we should be comparing it to the likes of Symfony and Zend Framework, rather than Joomla and Typo3.
If you want to go deeper with Drupal, you should understand how information flows between the system's layers. There are five main layers to consider:
This directional flow from bottom to top controls how Drupal works. And If you want some functionality not showing up, you can configure follow types below:
Overview about configs in drupal:
Default drupal will cache content of table variables and table system and load follow codes below:
SELECT cid, DATA, created, expire, serialized FROM cache_bootstrap WHERE cid IN ('variables'); SELECT cid, DATA, created, expire, serialized FROM cache_bootstrap WHERE cid IN ('bootstrap_modules') SELECT cid, DATA, created, expire, serialized FROM cache_bootstrap WHERE cid IN ('system_list') SELECT cid, DATA, created, expire, serialized FROM cache_bootstrap WHERE cid IN ('module_implements')
Hooks: Hook is a PHP function. They provide a way for a module to extend the functionality of another module.
refer:
Call Basic Hooks: hook_schema, hook_enable,hook_menu,hook_uninstall
/** * Implements hook_enable(). */ function comment_enable() { // Insert records into the node_comment_statistics for nodes that are missing. $query = db_select('node', 'n'); $query->leftJoin('node_comment_statistics', 'ncs', 'ncs.nid = n.nid'); $query->addField('n', 'created', 'last_comment_timestamp'); $query->addField('n', 'uid', 'last_comment_uid'); $query->addField('n', 'nid'); $query->addExpression('0', 'comment_count'); $query->addExpression('NULL', 'last_comment_name'); $query->isNull('ncs.comment_count'); db_insert('node_comment_statistics') ->from($query) ->execute(); } /** * Implements hook_uninstall(). */ function comment_uninstall() { // Delete comment_body field. field_delete_field('comment_body'); // Remove variables. variable_del('comment_block_count'); $node_types = array_keys(node_type_get_types()); foreach ($node_types as $node_type) { field_attach_delete_bundle('comment', 'comment_node_' . $node_type); variable_del('comment_' . $node_type); variable_del('comment_anonymous_' . $node_type); variable_del('comment_controls_' . $node_type); variable_del('comment_default_mode_' . $node_type); variable_del('comment_default_order_' . $node_type); variable_del('comment_default_per_page_' . $node_type); variable_del('comment_form_location_' . $node_type); variable_del('comment_preview_' . $node_type); variable_del('comment_subject_field_' . $node_type); } } /** * Implements hook_menu(). */ function comment_menu() { $items['admin/content/comment'] = array( 'title' => 'Comments', 'description' => 'List and edit site comments and the comment approval queue.', 'page callback' => 'comment_admin', 'access arguments' => array('administer comments'), 'type' => MENU_LOCAL_TASK | MENU_NORMAL_ITEM, 'file' => 'comment.admin.inc', ); // Tabs begin here. $items['admin/content/comment/new'] = array( 'title' => 'Published comments', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/content/comment/approval'] = array( 'title' => 'Unapproved comments', 'title callback' => 'comment_count_unpublished', 'page arguments' => array('approval'), 'access arguments' => array('administer comments'), 'type' => MENU_LOCAL_TASK, ); $items['comment/%'] = array( 'title' => 'Comment permalink', 'page callback' => 'comment_permalink', 'page arguments' => array(1), 'access arguments' => array('access comments'), ); $items['comment/%/view'] = array( 'title' => 'View comment', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); // Every other comment path uses %, but this one loads the comment directly, // so we don't end up loading it twice (in the page and access callback). $items['comment/%comment/edit'] = array( 'title' => 'Edit', 'page callback' => 'comment_edit_page', 'page arguments' => array(1), 'access callback' => 'comment_access', 'access arguments' => array('edit', 1), 'type' => MENU_LOCAL_TASK, 'weight' => 0, ); ................. Ư /** * Implements hook_schema(). */ function comment_schema() { $schema['comment'] = array( 'description' => 'Stores comments and associated data.', 'fields' => array( 'cid' => array( 'type' => 'serial', 'not null' => TRUE, 'description' => 'Primary Key: Unique comment ID.', ), 'pid' => array( 'type' => 'int', 'not null' => TRUE, 'default' => 0, 'description' => 'The {comment}.cid to which this comment is a reply. If set to 0, this comment is not a reply to an existing comment.', ), ...................... }
Define menu items and page callbacks:
Below are example code for hook_menu in node module
/** * Implements hook_menu(). */ function node_menu() { $items['admin/content'] = array( 'title' => 'Content', 'description' => 'Find and manage content.', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_admin_content'), 'access arguments' => array('access content overview'), 'weight' => -10, 'file' => 'node.admin.inc', ); $items['admin/content/node'] = array( 'title' => 'Content', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/reports/status/rebuild'] = array( 'title' => 'Rebuild permissions', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_configure_rebuild_confirm'), // Any user than can potentially trigger a node_access_needs_rebuild(TRUE) // has to be allowed access to the 'node access rebuild' confirm form. 'access arguments' => array('access administration pages'), 'type' => MENU_CALLBACK, 'file' => 'node.admin.inc', ); $items['admin/structure/types'] = array( 'title' => 'Content types', 'description' => 'Manage content types, including default status, front page promotion, comment settings, etc.', 'page callback' => 'node_overview_types', 'access arguments' => array('administer content types'), 'file' => 'content_types.inc', ); $items['admin/structure/types/list'] = array( 'title' => 'List', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['admin/structure/types/add'] = array( 'title' => 'Add content type', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_type_form'), 'access arguments' => array('administer content types'), 'type' => MENU_LOCAL_ACTION, 'file' => 'content_types.inc', ); $items['admin/structure/types/manage/%node_type'] = array( 'title' => 'Edit content type', 'title callback' => 'node_type_page_title', 'title arguments' => array(4), 'page callback' => 'drupal_get_form', 'page arguments' => array('node_type_form', 4), 'access arguments' => array('administer content types'), 'file' => 'content_types.inc', ); $items['admin/structure/types/manage/%node_type/edit'] = array( 'title' => 'Edit', 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/structure/types/manage/%node_type/delete'] = array( 'title' => 'Delete', 'page arguments' => array('node_type_delete_confirm', 4), 'access arguments' => array('administer content types'), 'file' => 'content_types.inc', ); $items['node'] = array( 'page callback' => 'node_page_default', 'access arguments' => array('access content'), 'menu_name' => 'navigation', 'type' => MENU_CALLBACK, ); $items['node/add'] = array( 'title' => 'Add content', 'page callback' => 'node_add_page', 'access callback' => '_node_add_access', 'file' => 'node.pages.inc', ); $items['rss.xml'] = array( 'title' => 'RSS feed', 'page callback' => 'node_feed', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); // @todo Remove this loop when we have a 'description callback' property. // Reset internal static cache of _node_types_build(), forces to rebuild the // node type information. node_type_cache_reset(); foreach (node_type_get_types() as $type) { $type_url_str = str_replace('_', '-', $type->type); $items['node/add/' . $type_url_str] = array( 'title' => $type->name, 'title callback' => 'check_plain', 'page callback' => 'node_add', 'page arguments' => array($type->type), 'access callback' => 'node_access', 'access arguments' => array('create', $type->type), 'description' => $type->description, 'file' => 'node.pages.inc', ); } $items['node/%node'] = array( 'title callback' => 'node_page_title', 'title arguments' => array(1), // The page callback also invokes drupal_set_title() in case // the menu router's title is overridden by a menu link. 'page callback' => 'node_page_view', 'page arguments' => array(1), 'access callback' => 'node_access', 'access arguments' => array('view', 1), ); $items['node/%node/view'] = array( 'title' => 'View', 'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10, ); $items['node/%node/edit'] = array( 'title' => 'Edit', 'page callback' => 'node_page_edit', 'page arguments' => array(1), 'access callback' => 'node_access', 'access arguments' => array('update', 1), 'weight' => 0, 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE, 'file' => 'node.pages.inc', ); $items['node/%node/delete'] = array( 'title' => 'Delete', 'page callback' => 'drupal_get_form', 'page arguments' => array('node_delete_confirm', 1), 'access callback' => 'node_access', 'access arguments' => array('delete', 1), 'weight' => 1, 'type' => MENU_LOCAL_TASK, 'context' => MENU_CONTEXT_INLINE, 'file' => 'node.pages.inc', ); $items['node/%node/revisions'] = array( 'title' => 'Revisions', 'page callback' => 'node_revision_overview', 'page arguments' => array(1), 'access callback' => '_node_revision_access', 'access arguments' => array(1), 'weight' => 2, 'type' => MENU_LOCAL_TASK, 'file' => 'node.pages.inc', ); $items['node/%node/revisions/%/view'] = array( 'title' => 'Revisions', 'load arguments' => array(3), 'page callback' => 'node_show', 'page arguments' => array(1, TRUE), 'access callback' => '_node_revision_access', 'access arguments' => array(1), ); $items['node/%node/revisions/%/revert'] = array( 'title' => 'Revert to earlier revision', 'load arguments' => array(3), 'page callback' => 'drupal_get_form', 'page arguments' => array('node_revision_revert_confirm', 1), 'access callback' => '_node_revision_access', 'access arguments' => array(1, 'update'), 'file' => 'node.pages.inc', ); $items['node/%node/revisions/%/delete'] = array( 'title' => 'Delete earlier revision', 'load arguments' => array(3), 'page callback' => 'drupal_get_form', 'page arguments' => array('node_revision_delete_confirm', 1), 'access callback' => '_node_revision_access', 'access arguments' => array(1, 'delete'), 'file' => 'node.pages.inc', ); return $items; }
function node_menu() { ...................... $items['node/%node'] = array( 'title callback' => 'node_page_title', 'title arguments' => array(1), // The page callback also invokes drupal_set_title() in case // the menu router's title is overridden by a menu link. 'page callback' => 'node_page_view', 'page arguments' => array(1), 'access callback' => 'node_access', 'access arguments' => array('view', 1), ); ...................... } /** * Menu callback; view a single node. */ function node_page_view($node) { // If there is a menu link to this node, the link becomes the last part // of the active trail, and the link name becomes the page title. // Thus, we must explicitly set the page title to be the node title. drupal_set_title($node->title); $uri = entity_uri('node', $node); // Set the node path as the canonical URL to prevent duplicate content. drupal_add_html_head_link(array('rel' => 'canonical', 'href' => url($uri['path'], $uri['options'])), TRUE); // Set the non-aliased path as a default shortlink. drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)))), TRUE); return node_show($node); }
Overview about Hook_schema:
Example code:
/** * Implements hook_schema(). */ function node_schema() { $schema['node'] = array( 'description' => 'The base table for nodes.', 'fields' => array( 'nid' => array( 'description' => 'The primary identifier for a node.', 'type' => 'serial', 'unsigned' => TRUE, 'not null' => TRUE, ), // Defaults to NULL in order to avoid a brief period of potential // deadlocks on the index. 'vid' => array( 'description' => 'The current {node_revision}.vid version identifier.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL, ), 'type' => array( 'description' => 'The {node_type}.type of this node.', 'type' => 'varchar', 'length' => 32, 'not null' => TRUE, 'default' => '', ), 'language' => array( 'description' => 'The {languages}.language of this node.', 'type' => 'varchar', 'length' => 12, 'not null' => TRUE, 'default' => '', ), 'title' => array( 'description' => 'The title of this node, always treated as non-markup plain text.', 'type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => '', ), 'uid' => array( 'description' => 'The {users}.uid that owns this node; initially, this is the user that created it.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'status' => array( 'description' => 'Boolean indicating whether the node is published (visible to non-administrators).', 'type' => 'int', 'not null' => TRUE, 'default' => 1, ), 'created' => array( 'description' => 'The Unix timestamp when the node was created.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'changed' => array( 'description' => 'The Unix timestamp when the node was most recently saved.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'comment' => array( 'description' => 'Whether comments are allowed on this node: 0 = no, 1 = closed (read only), 2 = open (read/write).', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'promote' => array( 'description' => 'Boolean indicating whether the node should be displayed on the front page.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'sticky' => array( 'description' => 'Boolean indicating whether the node should be displayed at the top of lists in which it appears.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), 'tnid' => array( 'description' => 'The translation set id for this node, which equals the node id of the source post in each set.', 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, 'default' => 0, ), 'translate' => array( 'description' => 'A boolean indicating whether this translation page needs to be updated.', 'type' => 'int', 'not null' => TRUE, 'default' => 0, ), ), 'indexes' => array( 'node_changed' => array('changed'), 'node_created' => array('created'), 'node_frontpage' => array('promote', 'status', 'sticky', 'created'), 'node_status_type' => array('status', 'type', 'nid'), 'node_title_type' => array('title', array('type', 4)), 'node_type' => array(array('type', 4)), 'uid' => array('uid'), 'tnid' => array('tnid'), 'translate' => array('translate'), ), 'unique keys' => array( 'vid' => array('vid'), ), 'foreign keys' => array( 'node_revision' => array( 'table' => 'node_revision', 'columns' => array('vid' => 'vid'), ), 'node_author' => array( 'table' => 'users', 'columns' => array('uid' => 'uid'), ), ), 'primary key' => array('nid'), ); ........ }
Basic hooks: hook_block_info, hook_block_configure, hook_block_save, hook_block_view
/** * Implements hook_block_info(). */ function node_block_info() { $blocks['syndicate']['info'] = t('Syndicate'); // Not worth caching. $blocks['syndicate']['cache'] = DRUPAL_NO_CACHE; $blocks['recent']['info'] = t('Recent content'); $blocks['recent']['properties']['administrative'] = TRUE; return $blocks; }
/** * Implements hook_block_configure(). */ function node_block_configure($delta = '') { $form = array(); if ($delta == 'recent') { $form['node_recent_block_count'] = array( '#type' => 'select', '#title' => t('Number of recent content items to display'), '#default_value' => variable_get('node_recent_block_count', 10), '#options' => drupal_map_assoc(array(2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 30)), ); } return $form; }
/** * Implements hook_block_save(). */ function node_block_save($delta = '', $edit = array()) { if ($delta == 'recent') { variable_set('node_recent_block_count', $edit['node_recent_block_count']); } }
/** * Implements hook_block_view(). */ function node_block_view($delta = '') { $block = array(); switch ($delta) { case 'syndicate': $block['subject'] = t('Syndicate'); $block['content'] = theme('feed_icon', array('url' => 'rss.xml', 'title' => t('Syndicate'))); break; case 'recent': if (user_access('access content')) { $block['subject'] = t('Recent content'); if ($nodes = node_get_recent(variable_get('node_recent_block_count', 10))) { $block['content'] = theme('node_recent_block', array( 'nodes' => $nodes, )); } else { $block['content'] = t('No content available.'); } } break; } return $block; }
hook_permission define user permissions for module. When hook_permission is defined, we can see permission settings at link /admin/people/permissions
/** * Implements hook_permission(). */ function node_permission() { $perms = array( 'bypass node access' => array( 'title' => t('Bypass content access control'), 'description' => t('View, edit and delete all content regardless of permission restrictions.'), 'restrict access' => TRUE, ), 'administer content types' => array( 'title' => t('Administer content types'), 'restrict access' => TRUE, ), 'administer nodes' => array( 'title' => t('Administer content'), 'restrict access' => TRUE, ), 'access content overview' => array( 'title' => t('Access the content overview page'), ), 'access content' => array( 'title' => t('View published content'), ), 'view own unpublished content' => array( 'title' => t('View own unpublished content'), ), 'view revisions' => array( 'title' => t('View content revisions'), ), 'revert revisions' => array( 'title' => t('Revert content revisions'), ), 'delete revisions' => array( 'title' => t('Delete content revisions'), ), ); // Generate standard node permissions for all applicable node types. foreach (node_permissions_get_configured_types() as $type) { $perms += node_list_permissions($type); } return $perms; } /** * Returns an array of node types that should be managed by permissions. * * By default, this will include all node types in the system. To exclude a * specific node from getting permissions defined for it, set the * node_permissions_$type variable to 0. Core does not provide an interface * for doing so, however, contrib modules may exclude their own nodes in * hook_install(). Alternatively, contrib modules may configure all node types * at once, or decide to apply some other hook_node_access() implementation * to some or all node types. * * @return * An array of node types managed by this module. */ function node_permissions_get_configured_types() { $configured_types = array(); foreach (node_type_get_types() as $type => $info) { if (variable_get('node_permissions_' . $type, 1)) { $configured_types[] = $type; } } return $configured_types; } /** * Helper function to generate standard node permission list for a given type. * * @param $type * The machine-readable name of the node type. * @return array * An array of permission names and descriptions. */ function node_list_permissions($type) { $info = node_type_get_type($type); // Build standard list of node permissions for this type. $perms = array( "create $type content" => array( 'title' => t('%type_name: Create new content', array('%type_name' => $info->name)), ), "edit own $type content" => array( 'title' => t('%type_name: Edit own content', array('%type_name' => $info->name)), ), "edit any $type content" => array( 'title' => t('%type_name: Edit any content', array('%type_name' => $info->name)), ), "delete own $type content" => array( 'title' => t('%type_name: Delete own content', array('%type_name' => $info->name)), ), "delete any $type content" => array( 'title' => t('%type_name: Delete any content', array('%type_name' => $info->name)), ), ); return $perms; }
$items['admin/structure/types/manage/%node_type/delete'] = array( 'title' => 'Delete', 'page arguments' => array('node_type_delete_confirm', 4), 'access arguments' => array('administer content types'), 'file' => 'content_types.inc', ); $items['node'] = array( 'page callback' => 'node_page_default', 'access arguments' => array('access content'), 'menu_name' => 'navigation', 'type' => MENU_CALLBACK, ); $items['node/add'] = array( 'title' => 'Add content', 'page callback' => 'node_add_page', 'access callback' => '_node_add_access', 'file' => 'node.pages.inc', ); $items['rss.xml'] = array( 'title' => 'RSS feed', 'page callback' => 'node_feed', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); // @todo Remove this loop when we have a 'description callback' property. // Reset internal static cache of _node_types_build(), forces to rebuild the // node type information. node_type_cache_reset(); foreach (node_type_get_types() as $type) { $type_url_str = str_replace('_', '-', $type->type); $items['node/add/' . $type_url_str] = array( 'title' => $type->name, 'title callback' => 'check_plain', 'page callback' => 'node_add', 'page arguments' => array($type->type), 'access callback' => 'node_access', 'access arguments' => array('create', $type->type), 'description' => $type->description, 'file' => 'node.pages.inc', ); } $items['node/%node'] = array( 'title callback' => 'node_page_title', 'title arguments' => array(1), // The page callback also invokes drupal_set_title() in case // the menu router's title is overridden by a menu link. 'page callback' => 'node_page_view', 'page arguments' => array(1), 'access callback' => 'node_access', 'access arguments' => array('view', 1), );
Research form hook in module user
/** * Implements hook_menu(). */ function user_menu() { ................ $items['user'] = array( 'title' => 'User account', 'title callback' => 'user_menu_title', 'page callback' => 'user_page', 'access callback' => TRUE, 'file' => 'user.pages.inc', 'weight' => -10, 'menu_name' => 'user-menu', ); ................ }
/** * Access callback for path /user. * * Displays user profile if user is logged in, or login form for anonymous * users. */ function user_page() { global $user; if ($user->uid) { menu_set_active_item('user/' . $user->uid); return menu_execute_active_handler(NULL, FALSE); } else { return drupal_get_form('user_login'); } }
/**user_login($form, &$form_state) * Form builder; the main user login form. * * @ingroup forms */ function user_login($form, &$form_state) { global $user; // If we are already logged on, go to the user page instead. if ($user->uid) { drupal_goto('user/' . $user->uid); } // Display login form: $form['name'] = array('#type' => 'textfield', '#title' => t('Username'), '#size' => 60, '#maxlength' => USERNAME_MAX_LENGTH, '#required' => TRUE, ); $form['name']['#description'] = t('Enter your @s username.', array('@s' => variable_get('site_name', 'Drupal'))); $form['pass'] = array('#type' => 'password', '#title' => t('Password'), '#description' => t('Enter the password that accompanies your username.'), '#required' => TRUE, ); $form['#validate'] = user_login_default_validators(); $form['actions'] = array('#type' => 'actions'); $form['actions']['submit'] = array('#type' => 'submit', '#value' => t('Log in')); return $form; }
/** * A FAPI validate handler. Sets an error if supplied username has been blocked. */ function user_login_name_validate($form, &$form_state) { if (isset($form_state['values']['name']) && user_is_blocked($form_state['values']['name'])) { // Blocked in user administration. form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name']))); } } /** * A validate handler on the login form. Check supplied username/password * against local users table. If successful, $form_state['uid'] * is set to the matching user ID. */ function user_login_authenticate_validate($form, &$form_state) { $password = trim($form_state['values']['pass']); if (!empty($form_state['values']['name']) && !empty($password)) { // Do not allow any login from the current user's IP if the limit has been // reached. Default is 50 failed attempts allowed in one hour. This is // independent of the per-user limit to catch attempts from one IP to log // in to many different user accounts. We have a reasonably high limit // since there may be only one apparent IP for all users at an institution. if (!flood_is_allowed('failed_login_attempt_ip', variable_get('user_failed_login_ip_limit', 50), variable_get('user_failed_login_ip_window', 3600))) { $form_state['flood_control_triggered'] = 'ip'; return; } $account = db_query("SELECT * FROM {users} WHERE name = :name AND status = 1", array(':name' => $form_state['values']['name']))->fetchObject(); if ($account) { if (variable_get('user_failed_login_identifier_uid_only', FALSE)) { // Register flood events based on the uid only, so they apply for any // IP address. This is the most secure option. $identifier = $account->uid; } else { // The default identifier is a combination of uid and IP address. This // is less secure but more resistant to denial-of-service attacks that // could lock out all users with public user names. $identifier = $account->uid . '-' . ip_address(); } $form_state['flood_control_user_identifier'] = $identifier; // Don't allow login if the limit for this user has been reached. // Default is to allow 5 failed attempts every 6 hours. if (!flood_is_allowed('failed_login_attempt_user', variable_get('user_failed_login_user_limit', 5), variable_get('user_failed_login_user_window', 21600), $identifier)) { $form_state['flood_control_triggered'] = 'user'; return; } } // We are not limited by flood control, so try to authenticate. // Set $form_state['uid'] as a flag for user_login_final_validate(). $form_state['uid'] = user_authenticate($form_state['values']['name'], $password); } } /** * The final validation handler on the login form. * * Sets a form error if user has not been authenticated, or if too many * logins have been attempted. This validation function should always * be the last one. */ function user_login_final_validate($form, &$form_state) { if (empty($form_state['uid'])) { // Always register an IP-based failed login event. flood_register_event('failed_login_attempt_ip', variable_get('user_failed_login_ip_window', 3600)); // Register a per-user failed login event. if (isset($form_state['flood_control_user_identifier'])) { flood_register_event('failed_login_attempt_user', variable_get('user_failed_login_user_window', 21600), $form_state['flood_control_user_identifier']); } if (isset($form_state['flood_control_triggered'])) { if ($form_state['flood_control_triggered'] == 'user') { form_set_error('name', format_plural(variable_get('user_failed_login_user_limit', 5), 'Sorry, there has been more than one failed login attempt for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password')))); } else { // We did not find a uid, so the limit is IP-based. form_set_error('name', t('Sorry, too many failed login attempts from your IP address. This IP address is temporarily blocked. Try again later or <a href="@url">request a new password</a>.', array('@url' => url('user/password')))); } } else { form_set_error('name', t('Sorry, unrecognized username or password. <a href="@password">Have you forgotten your password?</a>', array('@password' => url('user/password')))); watchdog('user', 'Login attempt failed for %user.', array('%user' => $form_state['values']['name'])); } } elseif (isset($form_state['flood_control_user_identifier'])) { // Clear past failures for this user so as not to block a user who might // log in and out more than once in an hour. flood_clear_event('failed_login_attempt_user', $form_state['flood_control_user_identifier']); } }
function user_login_submit($form, &$form_state) { global $user; $user = user_load($form_state['uid']); $form_state['redirect'] = 'user/' . $user->uid; user_login_finalize($form_state); }
hook_theme define all themes in module, and all themes will begin with prefix theme_, with these defines, we can call function theme(…) to display the theme
/** * Invoke a hook in all enabled modules that implement it. * * @param $hook * The name of the hook to invoke. * @param ... * Arguments to pass to the hook. * * @return * An array of return values of the hook implementations. If modules return * arrays from their implementations, those are merged into one array. */ function module_invoke_all($hook) { $args = func_get_args(); // Remove $hook from the arguments. unset($args[0]); $return = array(); foreach (module_implements($hook) as $module) { $function = $module . '_' . $hook; if (function_exists($function)) { $result = call_user_func_array($function, $args); if (isset($result) && is_array($result)) { $return = array_merge_recursive($return, $result); } elseif (isset($result)) { $return[] = $result; } } } return $return; }
/** * Save changes to a node or add a new node. * * @param $node * The $node object to be saved. If $node->nid is * omitted (or $node->is_new is TRUE), a new node will be added. */ function node_save($node) { $transaction = db_transaction(); try { // Load the stored entity, if any. if (!empty($node->nid) && !isset($node->original)) { $node->original = entity_load_unchanged('node', $node->nid); } field_attach_presave('node', $node); global $user; // Determine if we will be inserting a new node. if (!isset($node->is_new)) { $node->is_new = empty($node->nid); } // Set the timestamp fields. if (empty($node->created)) { $node->created = REQUEST_TIME; } // The changed timestamp is always updated for bookkeeping purposes, // for example: revisions, searching, etc. $node->changed = REQUEST_TIME; $node->timestamp = REQUEST_TIME; $update_node = TRUE; // Let modules modify the node before it is saved to the database. module_invoke_all('node_presave', $node); module_invoke_all('entity_presave', $node, 'node'); if ($node->is_new || !empty($node->revision)) { // When inserting either a new node or a new node revision, $node->log // must be set because {node_revision}.log is a text column and therefore // cannot have a default value. However, it might not be set at this // point (for example, if the user submitting a node form does not have // permission to create revisions), so we ensure that it is at least an // empty string in that case. // @todo: Make the {node_revision}.log column nullable so that we can // remove this check. if (!isset($node->log)) { $node->log = ''; } } elseif (!isset($node->log) || $node->log === '') { // If we are updating an existing node without adding a new revision, we // need to make sure $node->log is unset whenever it is empty. As long as // $node->log is unset, drupal_write_record() will not attempt to update // the existing database column when re-saving the revision; therefore, // this code allows us to avoid clobbering an existing log entry with an // empty one. unset($node->log); } // When saving a new node revision, unset any existing $node->vid so as to // ensure that a new revision will actually be created, then store the old // revision ID in a separate property for use by node hook implementations. if (!$node->is_new && !empty($node->revision) && $node->vid) { $node->old_vid = $node->vid; unset($node->vid); } // Save the node and node revision. if ($node->is_new) { // For new nodes, save new records for both the node itself and the node // revision. drupal_write_record('node', $node); _node_save_revision($node, $user->uid); $op = 'insert'; } else { // For existing nodes, update the node record which matches the value of // $node->nid. drupal_write_record('node', $node, 'nid'); // Then, if a new node revision was requested, save a new record for // that; otherwise, update the node revision record which matches the // value of $node->vid. if (!empty($node->revision)) { _node_save_revision($node, $user->uid); } else { _node_save_revision($node, $user->uid, 'vid'); $update_node = FALSE; } $op = 'update'; } if ($update_node) { db_update('node') ->fields(array('vid' => $node->vid)) ->condition('nid', $node->nid) ->execute(); } // Call the node specific callback (if any). This can be // node_invoke($node, 'insert') or // node_invoke($node, 'update'). node_invoke($node, $op); // Save fields. $function = "field_attach_$op"; $function('node', $node); module_invoke_all('node_' . $op, $node); module_invoke_all('entity_' . $op, $node, 'node'); // Update the node access table for this node. There's no need to delete // existing records if the node is new. $delete = $op == 'update'; node_access_acquire_grants($node, $delete); // Clear internal properties. unset($node->is_new); unset($node->original); // Clear the static loading cache. entity_get_controller('node')->resetCache(array($node->nid)); // Ignore slave server temporarily to give time for the // saved node to be propagated to the slave. db_ignore_slave(); } catch (Exception $e) { $transaction->rollback(); watchdog_exception('node', $e); throw $e; } }
⇒ We have 2 hooks are called:
module_invoke_all('node_presave', $node);//hook_node_presave module_invoke_all('entity_presave', $node, 'node');//hook_entity_presave
drupal theme start using original themes and templates of drupal and don't have any custom changes
layout.css logo.png screenshot.png stark.info
name = Stark description = This theme demonstrates Drupal's default HTML markup and CSS styles. To learn how to build your own theme and override Drupal's default code, see the <a href="http://drupal.org/theme-guide">Theming Guide</a>. package = Core version = VERSION core = 7.x stylesheets[all][] = layout.css ; Information added by drupal.org packaging script on 2012-05-02 version = "7.14" project = "drupal" datestamp = "1335997555"
Original datas which drupal theme stark using:
http://www.babies.vn/modules/system/system.base.css http://www.babies.vn/modules/system/system.menus.css http://www.babies.vn/modules/system/system.messages.css http://www.babies.vn/modules/system/system.theme.css http://www.babies.vn/modules/book/book.css http://www.babies.vn/sites/all/modules/date/date_api/date.css http://www.babies.vn/sites/all/modules/date/date_popup/themes/datepicker.1.7.css http://www.babies.vn/modules/field/theme/field.css http://www.babies.vn/modules/node/node.css http://www.babies.vn/modules/search/search.css http://www.babies.vn/modules/user/user.css http://www.babies.vn/sites/all/modules/views/css/views.css http://www.babies.vn/sites/all/modules/ckeditor/ckeditor.css http://www.babies.vn/sites/all/modules/ctools/css/ctools.css http://www.babies.vn/sites/all/modules/nice_menus/nice_menus.css http://www.babies.vn/sites/all/modules/nice_menus/nice_menus_default.css http://www.babies.vn/themes/stark/layout.css http://www.babies.vn/misc/jquery.js?v=1.4.4 http://www.babies.vn/misc/jquery.once.js?v=1.2 http://www.babies.vn/misc/drupal.js http://www.babies.vn/sites/default/files/languages/vi_GnJYyB-aL20IBG2Dax83cWnRkYnoPqmABr421jNP-Dg.js http://www.babies.vn/sites/all/modules/nice_menus/superfish/js/superfish.js http://www.babies.vn/sites/all/modules/nice_menus/superfish/js/jquery.bgiframe.min.js http://www.babies.vn/sites/all/modules/nice_menus/superfish/js/jquery.hoverIntent.minified.js http://www.babies.vn/sites/all/modules/nice_menus/nice_menus.js http://www.babies.vn/sites/all/modules/google_analytics/googleanalytics.js
$defaults = array( 'engine' => 'phptemplate', 'regions' => array( 'sidebar_first' => 'Left sidebar', 'sidebar_second' => 'Right sidebar', 'content' => 'Content', 'header' => 'Header', 'footer' => 'Footer', 'highlighted' => 'Highlighted', 'help' => 'Help', 'page_top' => 'Page top', 'page_bottom' => 'Page bottom', ), 'description' => '', 'features' => _system_default_theme_features(), 'screenshot' => 'screenshot.png', 'php' => DRUPAL_MINIMUM_PHP, 'stylesheets' => array(), 'scripts' => array(), );
modules\system\html.tpl.php modules\system\maintenance-page.tpl.php modules\system\page.tpl.php modules\system\region.tpl.php modules\block\block.tpl.php ....................
refer: https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme/7
Investigate custom codes in drupal theme professional_pro:
/** * Implements hook_theme(). */ function node_theme() { return array( 'node' => array( 'render element' => 'elements', 'template' => 'node', ), 'node_search_admin' => array( 'render element' => 'form', ), 'node_add_list' => array( 'variables' => array('content' => NULL), 'file' => 'node.pages.inc', ), 'node_preview' => array( 'variables' => array('node' => NULL), 'file' => 'node.pages.inc', ), 'node_admin_overview' => array( 'variables' => array('name' => NULL, 'type' => NULL), ), 'node_recent_block' => array( 'variables' => array('nodes' => NULL), ), 'node_recent_content' => array( 'variables' => array('node' => NULL), ), ); }
node node_search_admin node_add_list node_preview node_admin_overview node_recent_block node_recent_content
function theme_node_recent_content($variables) { $node = $variables['node']; $output = '<div class="node-title">'; $output .= l($node->title, 'node/' . $node->nid); $output .= theme('mark', array('type' => node_mark($node->nid, $node->changed))); $output .= '</div><div class="node-author">'; $output .= theme('username', array('account' => user_load($node->uid))); $output .= '</div>'; return $output; }
/** * override theme_node_recent_content */ function professional_pro_node_recent_content($variables) { $node = $variables['node']; $output = '<div class="node-title">'; $output .= l($node->title, 'node/' . $node->nid); $output .= theme('mark', array('type' => node_mark($node->nid, $node->changed))); $output .= '</div>'; return $output; }
name = professional_pro description = professional_pro Theme is a fix Drupal 7 theme with many regions. package = Core core = 7.x stylesheets[all][] = css/layout.css stylesheets[all][] = css/slider.css stylesheets[all][] = css/menu.css stylesheets[all][] = css/style.css scripts[] = js/script.js regions[help] = Help regions[header] = Header regions[front_content] = Front Content regions[top_first] = Top first column regions[top_second] = Top second column regions[top_third] = Top third column regions[top_fourth] = Top fourth column regions[slideshow] = Slideshow regions[top_content] = Top Content regions[content] = Content regions[sidebar_first] = Sidebar first regions[sidebar_second] = Sidebar second regions[bottom_first] = Bottom first column regions[bottom_second] = Bottom second column regions[bottom_third] = Bottom third column regions[bottom_fourth] = Bottom fourth column regions[footer_first] = Footer first column regions[footer_second] = Footer second column regions[footer_third] = Footer third column regions[footer_fourth] = Footer fourth column settings[social_links] = 1 settings[twitter_username] = "shanidkv" settings[facebook_username] = "zymphonies" regions[footer] = Footer settings[shortcut_module_link] = 0 ; Information added by drupal.org packaging script on 2012-12-09 version = "7.x-1.1" core = "7.x" project = "professional_pro" datestamp = "1355068718"
refer:
If you are running more than one Drupal site, you can simplify management and upgrading of your sites by using the multi-site feature. Multi-site allows you to share a single Drupal installation including:
This is particularly useful for managing the code since each upgrade only needs to be done once. Each site will have:
so each site will have its own content, settings, enabled modules, and enabled theme. However, the sites are sharing a code base and web document root, so there may be security concerns with multiple administrators
To create a new site using a shared Drupal code base you must complete the following steps:
If you want using www1.babies.vn and www2.babies.vn to access the configuration of sites/www.babies.vn, Edit sites/sites.php with below content:
$sites['www1.babies.vn'] = 'www.babies.vn'; $sites['www2.babies.vn'] = 'www.babies.vn';
Or If want moving sites/www.babies.vn to sites/babiesvn, you must change the sites/sites.php to:
$sites['www1.babies.vn'] = 'babiesvn'; $sites['www2.babies.vn'] = 'babiesvn';
php scripts/drupal.sh http://www.babies.vn/node
php scripts/password-hash.sh 8941362 password: 8941362 hash: $S$Dr/zxP8rFxQjRGsaRpaqcA.HElcEFfExJJ80N.jlnyesqeg.emwJ
UPDATE users SET pass ='$S$Dr/zxP8rFxQjRGsaRpaqcA.HElcEFfExJJ80N.jlnyesqeg.emwJ' WHERE uid = 1;
script drupal test only run on linux or windows with cygwin was installed
php scripts/run-tests.sh --php /usr/bin/php --url http://www.babies.vn --all
php scripts/run-tests.sh --php /usr/bin/php --url http://www.babies.vn --class BlockTestCase
Generates content for a Drupal 7 database to test the upgrade process
php ./scripts/dump-database-d7.sh
Editor with eclipse: Eclipse PDT(PHP Development Tool)
Editor with notepad++:
module install inc engine install inc engine theme
UPDATE variable SET VALUE='s:6:"bartik"' WHERE name = 'theme_default'; TRUNCATE cache; TRUNCATE cache_bootstrap; TRUNCATE cache_block; TRUNCATE cache_menu; TRUNCATE cache_form;
check module name in file .info(if filename is page_manager.info ⇒ module name is page_manager) and update below query
UPDATE system SET STATUS=1 WHERE name='ctools';
Note: Erase all cache tables before create new content type Or clear all caches in admin
Article:
Basic Page:
Below are content of node_type table after add Article and Basic Page:
type | name | base | module |
---|---|---|---|
article | Article | node_content | node |
page | Basic page | node_content | node |
Required: Install module taxonomy
Step1: Go to Structure → Taxonomy to add new Vocabulary name article_tags with list terms: Xã Hội, Kinh Tế …
Step2: Create custom field Term Taxonomy for article content type
⇒ from label tags will auto create below information:
Next you click Term reference and select Vocabulary with name article_tags for contenty Article Below are content of tables from database:
entity_type | bundle | deleted | entity_id | revision_id | language | delta | field_tags_tid |
---|---|---|---|---|---|---|---|
node | article | 0 | 26 | 26 | und | 0 | 4 |
node | article | 0 | 27 | 27 | und | 0 | 5 |
tid | vid | name | description | format | weight | uuid |
---|---|---|---|---|---|---|
4 | 1 | News | NULL | NULL | 0 | a1588461-ff28-486c-b066-61d6e5b41da4 |
5 | 1 | Design | NULL | NULL | 0 | bce05cc9-3f42-4866-bb68-041db771a815 |
TRUNCATE TABLE cache; TRUNCATE TABLE cache_admin_menu; TRUNCATE TABLE cache_block; TRUNCATE TABLE cache_bootstrap; TRUNCATE TABLE cache_field; TRUNCATE TABLE cache_filter; TRUNCATE TABLE cache_form; TRUNCATE TABLE cache_image; TRUNCATE TABLE cache_l10n_update; TRUNCATE TABLE cache_libraries; TRUNCATE TABLE cache_media_xml; TRUNCATE TABLE cache_menu; TRUNCATE TABLE cache_page; TRUNCATE TABLE cache_panels; TRUNCATE TABLE cache_path; TRUNCATE TABLE cache_rules; TRUNCATE TABLE cache_token; TRUNCATE TABLE cache_update; TRUNCATE TABLE cache_variable; TRUNCATE TABLE cache_views; TRUNCATE TABLE cache_views_data;
Go to apperance at the view Administration theme select:
All basic Informations will be updated in Configuration menu
Go to structure→blocks and click to configure main menu block, In the 'Main Menu' Block page, you input the Block Title is <none> and click button save
refer: https://www.drupal.org/node/17565
Search in drupal source the keyword 'node_' to see all the theme_hook_suggestions for node, for example:
$variables['theme_hook_suggestions'][] = 'node__' . $node->type; $variables['theme_hook_suggestions'][] = 'node__' . $node->nid;
⇒ we can use node–{node→type}.tpl.php to create custom template for any node type
The file node.tpl.php is used to theme certain types of nodes. To theme individual content types in different ways, you need to create a file node-[type].tpl.php in your theme's folder, where [type] is the machine readable name of the content type
For Drupal 6 (Use – for Drupal 7):
In Drupal 7 template files for block are searched in the following order:
If the block delta key uses a hyphen, (-), replace this with an underscore (_).
pattern file: field–[type|name[–content-type]|content-type].tpl.php Drupal will use the most specific template it finds:
Provides file upload fields
Provides User and Node reference fields
Views: Creates lists of entities, like content and users(The same view architecture in database engine)
DROP TABLE cache_views; DROP TABLE cache_views_data; DROP TABLE views_display; DROP TABLE views_view;
The Views main editor is where you'll spend most of your time. There are a dizzying amount of options available. Instead, think of these as two main functions:
refer: http://www.wdtutorials.com/2011/06/29/drupal-7-how-create-custom-template-view#.VGBhA_msXPo custom template will be updated in code or in database
Hit the Theme: Information under Advanced tab to display theme information dialog
And below are information of template file for view in theme information dialog
custom template for display node and comment in view:
$vars['theme_hook_suggestions'][] = 'node__view__' . $vars['node']->view->name; $vars['theme_hook_suggestions'][] = 'node__view__' . $vars['node']->view->name . '__' . $vars['node']->view->current_display; $vars['theme_hook_suggestions'][] = 'comment__view__' . $vars['node']->view->name; $vars['theme_hook_suggestions'][] = 'comment__view__' . $vars['node']->view->name . '__' . $vars['node']->view->current_display;
To active the changes in view we need to actions one of below methods:
The Page manager and Panels modules used to be one and the same project, originally created as an alternative to Drupal's block system. A simplified description of the two modules is:
We use features module to export and import features of drupal. Below are step by step to action:
rm -rf sites/all/modules/contact_faq