====== drupal 7 ====== Docs: * {{:php:drupalmoduledevelopmentpptv1-2.pptx|}} * https://wappalyzer.com/ * http://topdrops.org/ * http://www.drupalshowcase.com/ * {{:php:building-applications-with-drupal-120625123014-phpapp02.pdf|}} * {{:php:drupalasaframework-140420134717-phpapp02.pdf|}} api: * https://api.drupal.org ===== 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:\\ {{:php:drupal_flow_0.gif|}}\\ ==== Understand about drupal Dataflow ==== - **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. - **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. - **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 - **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. - **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 === {{:php:drupal_start.png|}}\\ === 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 ==== - Load Config * D:\web\babies\.htaccess * D:\web\babies\index.php * D:\web\babies\includes\bootstrap.inc * D:\web\babies\sites\default\settings.php - 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 - 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 - 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. {{:php:drupal-arc.jpg|}}\\ ==== Hook when user enable,disable,uninstall module ==== refer: * refer all files *.api.php, for example * module system: system.api.php, theme.api.php, form.api.php, language.api.php * module block: block.api.php * https://api.drupal.org/api/drupal/includes%21module.inc/group/hooks/7 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** {{:php:enable-install-module.png|}} /** * 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 {{:php:menu_execute_active_handler.png|}} 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%'; {{:php:hook-menu.png|}} * 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 {{:php:node_schema.png|}} * Index of table node {{:php:node_schema_index.png|}} ==== Hook for block ==== {{:php:hook-block.png|}}\\ 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; } {{:php:hook-block-info.png|}} * **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; } {{:php:hook-block-configure.png|}} * **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 {{:php:hook-permissions.png|}} * 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 ==== {{:php:form-user-login.png|}} Research form hook in module user - 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', ); ................ } - 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'); } } - **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; } {{:php:form-user.png|}} - 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 request a new password.', 'Sorry, there have been more than @count failed login attempts for this account. It is temporarily blocked. Try again later or request a new password.', 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 request a new password.', array('@url' => url('user/password')))); } } else { form_set_error('name', t('Sorry, unrecognized username or password. Have you forgotten your password?', 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']); } } - 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 {{:php:hook-theme.png|}} * Diagram hook_theme with nice menu {{:php:hook-menu-nice-menu.png|}} ===== 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 ==== * bootstrap: https://api.drupal.org/api/drupal/includes%21bootstrap.inc/7 * common: https://api.drupal.org/api/drupal/includes%21common.inc/7 * session: https://api.drupal.org/api/drupal/includes%21session.inc/7 * database: https://api.drupal.org/api/drupal/includes%21database%21database.inc/7 * file: https://api.drupal.org/api/drupal/includes%21file.inc/7 * drupal core hook: https://api.drupal.org/api/drupal/modules%21system%21system.api.php/7 ==== constants and global variables ==== * constants: https://api.drupal.org/api/drupal/constants * global variables: https://api.drupal.org/api/drupal/globals ===== drupal theme ===== {{:php:drupal-theme.png|}} ==== 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 Theming Guide. 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(), ); {{:php:stark-block-region.png|}} * 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 === {{:php:drupal-theme-override.png|}} * 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 = '
'; $output .= l($node->title, 'node/' . $node->nid); $output .= theme('mark', array('type' => node_mark($node->nid, $node->changed))); $output .= '
'; $output .= theme('username', array('account' => user_load($node->uid))); $output .= '
'; return $output; }
* override with new theme function: /** * override theme_node_recent_content */ function professional_pro_node_recent_content($variables) { $node = $variables['node']; $output = '
'; $output .= l($node->title, 'node/' . $node->nid); $output .= theme('mark', array('type' => node_mark($node->nid, $node->changed))); $output .= '
'; 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:professional_pro_theme.png|}} ===== Multisite Configuration ===== ==== Understand about multisite in drupal ==== refer: * https://www.drupal.org/documentation/install/multi-site 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 ==== - Step1: Generate password hash php scripts/password-hash.sh 8941362 password: 8941362 hash: $S$Dr/zxP8rFxQjRGsaRpaqcA.HElcEFfExJJ80N.jlnyesqeg.emwJ - 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: [[php:eclipsepdt#config_to_editor_drupal|Eclipse PDT(PHP Development Tool)]] Editor with notepad++: - Step1: Go to **Settings -> Style Configurator** - Step2: Then select Language **PHP** to edit **User ext** to values below: module install inc engine install inc engine theme {{:php:notepaddrupal.jpg|}} ==== 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 ^ |article|Article|node_content|node| |page|Basic page|node_content|node| === 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 {{:php:customfield.png|}} => 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 ^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 | ==== 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** - 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** - 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**. - config performance: Go to **configuration->development->performance** - configuration->media->file system: tmp - 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 **** and click button **save** ===== drupal7 custom template ===== refer: https://www.drupal.org/node/1089656 ==== 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: - field--field-name--content-type.tpl.php - field--content-type.tpl.php - field--field-name.tpl.php - 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. {{:php:views-ui-simple.png|}} === 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 {{:php:view-theme-information.png|}} And below are information of template file for view in **theme information** dialog {{:php:view-theme-template.png|}} 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: - Step1 **Export**: Go to structure->feature, click tab **Create Feature** * First we select content type contact and faq with name contact_faq: {{:php:features-export.png|}} * Next we click button download feature -> we will be received the file contact_faq.tar which define new module for these features - 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 - Step3: After import, we will remove the module contact_faq: rm -rf sites/all/modules/contact_faq