Site Tools


php:drupal

drupal 7

Drupal Overview

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.

Drupal Dataflow

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:

Understand about drupal Dataflow

  1. data pool: At the base of the system is the collection of nodes—the data pool. Before anything can be displayed on the site, it must be input as data.
  2. Modules: The next layer up is where modules live. Modules are functional plugins that are either part of the Drupal core (they ship with Drupal) or they are contributed items that have been created by members of the Drupal community. Modules build on Drupal's core functionality, allowing you to:
    • customize the data items (fields) on your node types;
    • set up e-commerce;
    • programmatically sorting and display of content (custom output controlled by filters you define);
    • and more. There are thousands of different options within the fast-growing repository of contributed Drupal modules. They represent the innovation and collaborative effort of everyone from individuals to large corporations.
  3. blocks and menus: At the next layer, we find blocks and menus.
    • Blocks often provide the output from a module or can be created to display whatever you want, and then can be placed in various spots in your template (theme) layout. Blocks can be configured to output in various ways, as well as only showing on certain defined pages, or only for certain defined users.
    • Menus are navigators in Drupal, which defines the content coming on each defined menu path (relative url). Menus are core element of drupal which gives all the pages created in Drupal
  4. user permissions: Next are user permissions. This is where settings are configured to determine what different kinds of users are allowed to do and see. Permissions are defined for various roles, and in turn, users are assigned to these roles in order to grant them the defined permissions.
  5. Template: On the top layer is the site theme (the “skin”). This is made up predominantly of XHTML and CSS, with some PHP variables intermixed, so Drupal-generated content can go in the appropriate spots. Also included with each theme is a set of functions that can be used to override standard functions in the modules in order to provide complete control over how the modules generate their markup at output time. Templates can also be assigned on-the-fly based on user permissions.

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:

  • A: You uploaded the module into the system but have not activated it yet
  • B: The module is installed and activated, but you still don’t see what you want on your site, because you forget to place the block
  • C: The module is installed and activated, but you still don’t see what you want on your site, because your user permission settings conflicting with what you want and your users are not set to see the output

drupal startup

(drupal_bootstrap) and config

diagram


load system config

Overview about configs in drupal:

  • config\sites\default\setting.php: Config connect to database
  • Get initial variables in database:Table variables(before DRUPAL_BOOTSTRAP_FULL)
  • Get config for loading modules: Table system

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')

sequence load files

  1. Load Config
    • D:\web\babies\.htaccess
    • D:\web\babies\index.php
    • D:\web\babies\includes\bootstrap.inc
    • D:\web\babies\sites\default\settings.php
  2. Load core system
    • D:\web\babies\includes\cache.inc
    • D:\web\babies\includes\database\database.inc
    • D:\web\babies\includes\lock.inc
    • D:\web\babies\includes\database\mysql\database.inc
    • D:\web\babies\includes\database\query.inc
    • D:\web\babies\includes\database\mysql\query.inc
    • D:\web\babies\includes\database\select.inc
    • D:\web\babies\includes\module.inc
    • D:\web\babies\sites\all\modules\devel\devel.module
    • D:\web\babies\includes\session.inc
    • D:\web\babies\modules\user\user.module
    • D:\web\babies\includes\entity.inc
    • D:\web\babies\includes\language.inc
    • D:\web\babies\includes\locale.inc
    • D:\web\babies\includes\common.inc
    • D:\web\babies\includes\path.inc
    • D:\web\babies\includes\theme.inc
    • D:\web\babies\includes\pager.inc
    • D:\web\babies\includes\menu.inc
    • D:\web\babies\includes\tablesort.inc
    • D:\web\babies\includes\file.inc
    • D:\web\babies\includes\stream_wrappers.inc
    • D:\web\babies\includes\unicode.inc
    • D:\web\babies\includes\image.inc
    • D:\web\babies\includes\form.inc
    • D:\web\babies\includes\mail.inc
    • D:\web\babies\includes\actions.inc
    • D:\web\babies\includes\ajax.inc
    • D:\web\babies\includes\token.inc
    • D:\web\babies\includes\errors.inc
  3. Load modules
    • D:\web\babies\modules\block\block.module
    • D:\web\babies\modules\comment\comment.module
    • D:\web\babies\modules\dashboard\dashboard.module
    • D:\web\babies\sites\all\modules\devel\devel_generate\devel_generate.module
    • D:\web\babies\modules\field\field.module
    • D:\web\babies\modules\field\field.crud.inc
    • D:\web\babies\modules\field\field.default.inc
    • D:\web\babies\modules\field\field.info.inc
    • D:\web\babies\modules\field\field.multilingual.inc
    • D:\web\babies\modules\field\field.attach.inc
    • D:\web\babies\modules\field\field.form.inc
    • D:\web\babies\modules\field\modules\field_sql_storage\field_sql_storage.module
    • D:\web\babies\modules\file\file.module
    • D:\web\babies\modules\file\file.field.inc
    • D:\web\babies\modules\filter\filter.module
    • D:\web\babies\modules\image\image.module
    • D:\web\babies\modules\image\image.field.inc
    • D:\web\babies\modules\locale\locale.module
    • D:\web\babies\modules\menu\menu.module
    • D:\web\babies\modules\node\node.module
    • D:\web\babies\modules\field\modules\options\options.module
    • D:\web\babies\modules\search\search.module
    • D:\web\babies\modules\system\system.module
    • D:\web\babies\modules\taxonomy\taxonomy.module
    • D:\web\babies\modules\field\modules\text\text.module
    • D:\web\babies\modules\toolbar\toolbar.module
  4. Load template
    • D:\web\babies\profiles\standard\standard.profile
    • D:\web\babies\themes\engines\phptemplate\phptemplate.engine
    • D:\web\babies\themes\professional_pro\template.php
    • D:\web\babies\sites\all\modules\devel\krumo\class.krumo.php
    • D:\web\babies\themes\professional_pro\templates\page.tpl.php
    • D:\web\babies\modules\search\search-block-form.tpl.php
    • D:\web\babies\modules\block\block.tpl.php
    • D:\web\babies\modules\system\region.tpl.php
    • D:\web\babies\themes\professional_pro\templates\node.tpl.php
    • D:\web\babies\modules\user\user-picture.tpl.php
    • D:\web\babies\modules\block\block.tpl.php
    • D:\web\babies\modules\system\region.tpl.php
    • D:\web\babies\modules\toolbar\toolbar.tpl.php
    • D:\web\babies\themes\professional_pro\templates\html.tpl.php

drupal modules and Hooks

Hooks: Hook is a PHP function. They provide a way for a module to extend the functionality of another module.

  • Pattern: modulename_hookname.
  • For example, if you want to do some operation on user login, say sending an email to administrator on user logins into the site, then no need to change the code in user module:
    • Implement hook_user on your own module say “alert” by defining function in your module file called alert_user
    • And write code to send an email to administrator here.


Hook when user enable,disable,uninstall module

refer:

Call Basic Hooks: hook_schema, hook_enable,hook_menu,hook_uninstall

  • User enable(or install) module comment will auto call hooks: comment_enable, comment_schema, comment_menu
  • user disable module comment will auto call hooks: comment_disable, comment_menu
  • User uninstall module comment will auto call hooks: comment_uninstall, comment_schema

/**
 * 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.',
      ),
......................      
}

Hook_menu

Define menu items and page callbacks:

  • This hook enables modules to register paths in order to define how URL requests are handled.
  • Hook_menu will be called when user enable, disable or uninstall module.
  • After module was enabled, drupal will update the table menu_router to define the information of module base on defines in hook_menu

Below are example code for hook_menu in node module

  • Code define paths in module node
    /**
     * 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;
    }
  • Information of node_menu register paths in table menu_router
    SELECT * FROM menu_router WHERE path LIKE 'node%';

  • Below are implement of menu $items['node/%node']:
    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);
    }

Hook_schema

Overview about Hook_schema:

  • A Drupal schema definition is an array structure representing one or more tables and their related keys and indexes.
  • By implementing hook_schema() and specifying the tables your module declares, you can easily create and drop these tables on all supported database engines

Example code:

  • example code define for table node in hook node_schema:
    /**
     * 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'),
      );
    ........
    }  
  • information of node table in database
    • Table node

  • Index of table node

Hook for block


Basic hooks: hook_block_info, hook_block_configure, hook_block_save, hook_block_view

  • hook_block_info will auto call when admin go to /admin/structure/block. And below are example for module node:
    /**
     * 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;
    }

  • hook_block_config define configuration form for a block. It will call when admin config the block, for example: /admin/structure/block/manage/node/recent/configure. And below are example for module node
    /**
     * 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;
    }

  • hook_block_save will be called when admin save block configuration.Below are example code node_block_save
    /**
     * 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']);
      }
    }
  • hook_block_view will be called when block was displayed in layout. The display content will be get in variable $block['content'] which hook_block_view set
    /**
     * 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;
    }

Permissions, Access Control: hook_permission

hook_permission define user permissions for module. When hook_permission is defined, we can see permission settings at link /admin/people/permissions

  • Example hook_permisson for node module:
    /**
     * 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;
    }
  • permission settings at link /admin/people/permissions for node module

  • define permission in hook node_menu with parameter 'access arguments' Or 'access callback' :
    $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),
    );

Form

Research form hook in module user

  1. Define 'page callback' for menu /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',
      );
    ................
    }
  2. implement 'page callback' for menu /user: 'user_page'
    /**
     * 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');
      }
    }
  3. drupal_get_form('user_login') will call auto define form function user_login($form, &$form_state):
    /**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;
    }

  4. User Submit user, pass → valiate functions will be called to check data validated
    /**
     * 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']);
      }
    }
  5. If all function validate are OK, submit form will be called
    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);
    }

theme hooks

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

  • Diagram hook_theme with node module
  • Diagram hook_theme with nice menu

Define new hooks in drupal

  • Using function module_invoke_all in module.inc to call hook implementations for new hooks
    /**
     * 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;
    }
  • Below are codes to define hooks will be called when node_save($node) implement
    /**
     * 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

core drupal API, constants and global variables

core drupal API

constants and global variables

drupal theme

drupal theme stark

drupal theme start using original themes and templates of drupal and don't have any custom changes

  • list files in theme stark
    layout.css
    logo.png
    screenshot.png
    stark.info
  • content of 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:

  • stark using original css and js files of drupal
    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
  • drupal using default layout(regions) of drupal
    $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(),
    ); 

  • stark using original template files of drupal(alll files *.tpl.php)
    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
    ....................
  • start using original theme_* funnction of drupal

custom drupal theme

refer: https://api.drupal.org/api/drupal/includes%21theme.inc/function/theme/7

Investigate custom codes in drupal theme professional_pro:

  • Professional theme
  • HTML5 & super clean markup
  • Social link
  • Fixed Width
  • 1-column and 2-columns layout
  • Implementation of nivo slider
  • Multi-level drop-down menus (Multilingual menu)
  • A total of 20 regions
  • Drupal standards compliant
  • Custom front-page with 4 block regions
  • Minimal design and nice typography
  • Supported standard theme features: site logo, site name, site, user pictures in comments, user pictures in nodes

How to override theme_* functions and *.tpl.php in drupal

  • How to create list theme hooks for $cache:'theme_registry:build:modules': list theme hooks will be defined in function $modulename_theme(hook_theme).
    • Below are list theme hooks defined for module node:
      /**
       * 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),
          ),
        );
      }
    • Base on node_theme: we have list hook name for themes in node module:
      node
      node_search_admin
      node_add_list
      node_preview
      node_admin_overview
      node_recent_block
      node_recent_content
  • Drupal override the old theme function 'theme_$hook' with new theme function 'professional_pro_$hook' If this function exists. Below are example for node module, we can override the function theme_node_recent_content with new function:
    • old theme function:
      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 with new theme function:
      /**
       * 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;
      }
  • Drupal will call professinal_pro_preprocess_$hook and professinal_pro_process_$hook If these functions exists
  • You can add files *.tpl.php to directory 'theme\professional_pro' to replace the original files in drupal

change css,js and custom layout(region) in .info

  • [theme\professional_pro\professional_pro.info]
    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"
    
  • In file page.tpl.php define new positions for custom regions
    <?php if($page['top_first']): ?>
    <div class="column">
      <?php print render($page['top_first']); ?>
    </div>
    <?php endif; ?>
     
    <?php if($page['top_second']): ?>
    <div class="column">
      <?php print render($page['top_second']); ?>
    </div>

Multisite Configuration

Understand about multisite in drupal

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:

  • core code
  • contributed modules
  • and themes

This is particularly useful for managing the code since each upgrade only needs to be done once. Each site will have:

  • Its own database
  • and Its own configuration settings

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

Steps to create new site

To create a new site using a shared Drupal code base you must complete the following steps:

  • Create a new database for the site (if there is already an existing database you can also use this by defining a prefix in the installation procedure).
  • Create a new subdirectory of the 'sites' directory with the name of your new site (see below for information on how to name the subdirectory).For example: sites/wwww.babies.vn
  • Copy the file sites/default/default.settings.php into the subdirectory(sites/www.babies.vn) you created in the previous step. Rename the new file to settings.php.
  • Adjust the permissions of the new site directory, and grant write permissions on the configuration file (sites/default/default.settings.php).
  • Make symbolic links if you are using a subdirectory such as example.com/subdir and not a subdomain such as subd.example.com (see the subdirectory multi-site section below for details).
  • In a Web browser, navigate to the URL of the new site and continue with the standard Drupal installation procedure (if you get an infinite redirection loop, check if the file install.php exists the document root).

Option: Edit directory alias for site in sites/sites.php

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';

Drupal scripts

Run drupal with PHP CLI

php scripts/drupal.sh http://www.babies.vn/node

Change drupal admin password

  1. Step1: Generate password hash
    php scripts/password-hash.sh 8941362
    password: 8941362               hash: $S$Dr/zxP8rFxQjRGsaRpaqcA.HElcEFfExJJ80N.jlnyesqeg.emwJ
  2. Step2: Update password in drupal database:
    UPDATE users SET pass ='$S$Dr/zxP8rFxQjRGsaRpaqcA.HElcEFfExJJ80N.jlnyesqeg.emwJ' WHERE uid = 1;

Run drupal test

script drupal test only run on linux or windows with cygwin was installed

  • Run check all drupal modules:
    php scripts/run-tests.sh --php /usr/bin/php --url http://www.babies.vn --all
  • Check module BlockTestCase
    php scripts/run-tests.sh --php /usr/bin/php --url http://www.babies.vn --class BlockTestCase

Dump drupal database

Generates content for a Drupal 7 database to test the upgrade process

php ./scripts/dump-database-d7.sh

Drupal basic updates

Drupal editor

Editor with eclipse: Eclipse PDT(PHP Development Tool)

Editor with notepad++:

  1. Step1: Go to Settings → Style Configurator
  2. Step2: Then select Language PHP to edit User ext to values below:
    module install inc engine  install inc engine theme

Update default theme in database

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;

Enable drupal module in database

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';

Step by step create content types in theme

Note: Erase all cache tables before create new content type Or clear all caches in admin

Artile and Basic Page

Article:

  • Publish Options:
    • Published
    • Promoted to front page
  • Display Settings:
    • Display author and date information.

Basic Page:

  • Publish Options:
    • Published
  • Edit Machine name: page ⇒ content type

Below are content of node_type table after add Article and Basic Page:

type name base module
articleArticlenode_contentnode
pageBasic pagenode_contentnode

create custom field Term Taxonomy(Category) for article content type

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:

  • machine name field_tags
  • talbe field_data_field_tags

Next you click Term reference and select Vocabulary with name article_tags for contenty Article Below are content of tables from database:

  • table field_data_field_tags
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
  • table taxonomy_term_data with tid=4 or tid=5
tidvidnamedescriptionformatweightuuid
4 1 News NULL NULL 0 a1588461-ff28-486c-b066-61d6e5b41da4
5 1 Design NULL NULL 0 bce05cc9-3f42-4866-bb68-041db771a815

drupal clear database cache

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;

change administration theme

Go to apperance at the view Administration theme select:

  • Administration theme: Seven
  • Click to Check Use the administration theme when editing or creating content

change basic informations(site name, email,slogan,homepage, language, performance) in system configuration

All basic Informations will be updated in Configuration menu

  1. change site information(site name, e-mail address, slogan, default front page(homepage), and number of posts per page): Go to Configuration→System→Site Information
  2. change language:
    • Required: Install Locale module before configure
    • Go to configuration→Regional and Language
      • Using Translate interface to import .po file
      • Using Languages for Configure languages for content and the user interface.
  3. config performance: Go to configuration→development→performance
  4. configuration→media→file system: tmp
  5. configuration→content authoring→text formats: configure full HTML → remove all filters

disable title of Main 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

drupal7 custom template

custom theme node base on content type

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):

  • node-story.tpl.php: Themes only Story type nodes.
  • node-page.tpl.php: Themes only Page type nodes. Note that this is different from page.tpl.php which controls the layout of the entire page including header, sidebars and so on.
  • node-forum.tpl.php: Themes only Forum type nodes.
  • node-book.tpl.php: Themes only Book type nodes.

custom block and region template

In Drupal 7 template files for block are searched in the following order:

  • block–block–[block-id].tpl.php
  • block–[module]–[delta].tpl.php
  • block–[module].tpl.php
  • block–[region].tpl.php
  • block.tpl.php

If the block delta key uses a hyphen, (-), replace this with an underscore (_).

custom field template

pattern file: field–[type|name[–content-type]|content-type].tpl.php Drupal will use the most specific template it finds:

  1. field–field-name–content-type.tpl.php
  2. field–content-type.tpl.php
  3. field–field-name.tpl.php
  4. field–field-type.tpl.php

Drupal custom modules

drupal custom fields

Field (core) and Field UI (core)

  • Field: Provides infrastructure to add fields to entities
  • Field UI: Provides the user interface to add fields to entity forms

File (core)

Provides file upload fields

entity

References

Provides User and Node reference fields

ctools and views

Views: Creates lists of entities, like content and users(The same view architecture in database engine)

  • Views, at its most fundamental level is a database query builder: It allows us to construct database queries using a user-friendly interface, and subsequently, present the result to the user in a variety of manners. The query builder supports a number of components including:
    • Filters which conditionally refine the query to return a more accurate result set. For example, as we will customarily only require nodes that have been published, we can add a filter to the View stating that the result set should only contain published nodes. They can optionally also be exposed to the user as a form element.
    • Contextual filters which are usually dynamic parameters passed to the query at runtime such as the elements of the URL of the page being displayed.
    • Relationships which are usually used to connect two different database tables together based on the relationship between two of their elements.
    • Field specification which describes the fields to be returned as part of the result set.
    • Sorting support which specifies how the results are to be sorted.
    • Pagination support which limits the number of results returned.
    • Support for distinct results to limit duplicates in the result set.
  • Additionally, Views allows administrators to create multiple Displays for each View. Displays allow the efficient representation of the same query in a variety of different ways. For example, a list of nodes can be presented both as content on a page or as content in a block, and so on.

uninstall view database

DROP TABLE cache_views;
DROP TABLE cache_views_data;
DROP TABLE views_display;
DROP TABLE views_view;

select, filter and format display in 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:

  • What to select and filter from your database.
  • How to format the information and where to display it.

custom template in view

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;

experience

To active the changes in view we need to actions one of below methods:

  • Clear cache in configuration→performance
  • In view, you go to Advanced→Information: click Rescan template in view

Page manager and panels

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:

  • Page manager is a tool for defining new, arbitrary paths on your Drupal site, and configuring how that page should look and work. It is possible not only to define new paths, but also to override some of the paths defined by other modules. One important part of the Page manager functionality is to collect and send contexts, contextual information that may affect the page.
  • Panels is a tool for splitting the main content on a Drupal site into several regions and defining what content each region should contain.

Features module

We use features module to export and import features of drupal. Below are step by step to action:

  1. Step1 Export: Go to structure→feature, click tab Create Feature
    • First we select content type contact and faq with name contact_faq:
    • Next we click button download feature → we will be received the file contact_faq.tar which define new module for these features
  2. Step2 Import: We will extract the contact_faq.tar to sites/all/modules and install this module ⇒ the module will import all features to database
  3. Step3: After import, we will remove the module contact_faq:
    rm -rf sites/all/modules/contact_faq
php/drupal.txt · Last modified: 2022/10/29 16:15 by 127.0.0.1