使用开源软件设计、开发和部署协作型 Web 站点,第 5 部分: Drupal 入门

来源:百度文库 编辑:神马文学网 时间:2024/04/29 20:51:14

文档选项

将此页作为电子邮件发送

讨论
级别: 中级
Alister Lewis-Bowen (alister@us.ibm.com), 高级软件工程师, IBM
Stephen Evanchik (evanchik@us.ibm.com), 软件工程师, IBM
Louis Weitzman (louisw@us.ibm.com), 高级软件工程师, IBM
2007 年 4 月 13 日
在这个系列 中,IBM? Internet Technology Group 要使用一套可免费获得的软件来设计、开发和部署一个完整的社区 Web 站点。如果您按照先前文章中的说明进行操作,现在应该已经有了一个一般性的 Drupal 安装,可以开始添加内容并修改样式了。本文介绍在开发 Web 站点时使用的 Drupal 编程模型,描述不同类型的内容、使用模块开发新特性、实现挂钩来启用这些模块以及站点 URL 设计。
本文将概述 Drupal 内容管理系统。我们描述常用的构建块并讨论 Drupal 方式的一些共同假设。在阅读本文和后续文章时,理解核心概念和基本Drupal 术语 是有帮助的。
Drupal 内容管理系统在一个数据库中维护它的内容。在这个数据库中,内容存储为节点(node) 和其他高级对象,比如用户和评论。有一些不同的预定义节点类型,包括新闻、blog 和投票。
Drupal 构造的页面包含一段或更多的信息,这些信息采用节点、区块和其他条目的形式。页面通常组织为中间的内容栏、左右边栏、页眉和页脚。除了中间的内容栏之外,其他区域都是可选的。
中间栏用来显示站点的主要内容;可选区域用来显示附加的内容。Drupal 使用区块(block) 用小段信息填充可选区域。可选区域通常包含导航链接(例如,最流行的节点)和其他简短的内容。与任何内容一样,区块可以依赖于用户的角色,从而提供定制的信息视图。
Drupal 最重要的特性之一是,通过编写定制的节点模块(node module),能够对可用的节点类型进行扩展,比如您的应用程序特有的内容。模块是对 Drupal 的扩展,它们实现来自一个预定义接口的一个或多个挂钩(hook)。挂钩定义用户权限,与数据库进行交互,并提供一个编辑内容的界面。
在本系列的下一篇文章中,将创建一个定制的模块来存储和显示在线社区的公告。
菜单系统(menu system) 控制 Web 站点的导航,可以通过用户界面对它进行充分地定制。与之相反,菜单挂钩函数控制如何组成 URL、如何转换 URL 以及特定的 URL 将调用什么函数。Drupal 的初学者常常被这个挂钩函数的名称误导,因为它实际上与菜单无关。所以,在 Drupal 中处理 “菜单” 时,这是一个容易引起误解的地方。
一个系统根据模板对页面进行主题化(theme),从而将内容和表示方式隔离开。通过定义一套模板(即 tpl)文件和主题函数,可以轻松地构造和样式化大多数内容。
节点可以组织成分类(category 或taxonomy )。论坛(forum)就是分类中层次化内容的一个例子。
对所有内容的访问都要通过一个权限系统,从而控制对 Web 站点上的内容的访问和编辑。
一定要理解,我们提供这些步骤是为了实现在线社区站点所需的函数。不应该把这些信息理解为在开发成功的或功能性的 Drupal 站点时必须遵循的严格的开发原则。在开发您的站点时,您应以本文作为参考,根据自己的需要采用不同的步骤。
Drupal 中的一个重要概念是,所有内容都存储为节点。节点是系统的基本构造块,提供了对 Drupal 中存储的内容进行扩展的基础。开发人员可以创建新的节点模块,从而根据自己的需要在数据库中定义和存储额外的字段。节点按照类型(type) 进行分类。对于每种节点类型,可以根据它的用例以不同的方式进行操作和显示。标准节点类型包括:
页面(page)
用来显示内容的简单节点(通过使用 PHP,可以动态地更新内容。通过包含 PHP,任何内容都可以是动态的)
blog 文章(blog entry)
用来维护在线日记(即 weblog)的节点
论坛(forum)
节点及其评论的集合(这些节点通过分配一个分类项组合在一起)
新闻(story)
在特定日期之后就会过期的一般性页面(这些页面与普通页面相似,但是可以有不同的样式)
评论(comment)
这是一种特殊类型的内容,它允许用户对其他节点定义的内容进行评论(评论不是一种节点类型,它们存储在数据库中一个单独的表中)
数据库记录节点的基本信息,包括标题、摘要(teaser 或 abstract)和主体。还记录关于创建者、创建时间和状态的信息。如果您的应用程序需要任何额外的信息,那么一般来说需要创建新的节点类型,这种新节点将信息写入专门为它定义的数据库表中。
Drupal 实现了一个修订系统来管理对节点内容的修改。这使模块开发人员可以在专门用于节点修订的表中为节点的每次更新建立一个新的数据库记录。可以在Drupal revisions overhaul 了解更多信息。
Drupal 的分类系统(taxonomy system) 可以对节点进行分类,这样就能够对显示的 Web 页面上的节点内容进行组织。这个分类还可以用来修改 Web 站点的导航。
分类由标记(即项(term))来定义,分类项的集合可以组织成词汇表。Drupal 可以自动地按照分类项对节点内容进行分类,也可以使用相关联的词汇表对节点内容进行手工分类。Drupal 还允许自由地设置标记(free tagging),用户可以为节点内容定义他们自己的分类项。
通过使用分类模块提供的各种组织性函数,模块开发人员可以利用按照分类项分类的节点。这个模块还提供了一些函数,让开发人员可以根据节点内容的分类在页面上添加导航。
评论系统(Comment system) 是 Drupal 中的另一个特性。可以将节点配置为接受按线索组织的评论,这些评论是由具有适当权限的用户组发表的。这使用户能够对 Web 页面上显示的内容发表评论。通常,评论链接会出现在论坛主题或 weblog 文章中。




回页首
区块(Block) 是小的自含的信息单元,通常显示在页面的导航区域或边栏中,但是也可以放在页面上的任何地方。它们提供小的嵌入在其他节点中的信息视图。模块提供用来显示其内容的基本区块。管理员可以基于现有的区块创建新的区块,或者编写 PHP 代码来直接查询并显示区块中的内容。
在图 1 所示的配置页面中,可以:
启用或禁用已注册的区块。
通过分配权值,排列信息的显示次序(权值为 -10 到 10 的值,编号越小的条目在组中越高)。
将区块放在左右边栏、页眉、页脚或内容区域中。





回页首
模块(Module) 是扩展 Drupal 的主要机制。它们实现一个定义良好的接口,使新模块和系统能够相互交互。Drupal 将这个接口中的函数称为挂钩(hook)。Drupal 挂钩分成为三类;它们在模块中有三种作用:
身份验证(Authentication)
提供额外的用户身份验证机制
核心(Core)
与核心 Drupal 代码进行交互
节点(Node)
向系统提供新的节点类型
通常,如果需要以任何方式扩展 Drupal,就要创建新模块。需要创建模块的一种常见情况是,数据库中的标准节点表不包含应用程序所需的信息。实现节点挂钩的模块很容易利用扩展的数据库模式存储它所需的任何信息。一定要理解一点:模块不必实现 Drupal 的所有挂钩;它只需实现那些实现扩展所需的挂钩。
创建了模块之后,必须启用模块并向定义的各个角色分配访问特权。图 2 显示用来启用模块的管理员界面(administer > modules)。在这里,我们启用了 announcement 和 comment 模块。这个管理员界面使用复选框来启用或禁用各个模块,比如我们定制的 announcement 模块和系统提供的 comment 模块。

为了通过程序定义模块,需要按照预定义函数的语法创建特定的函数。这些挂钩使系统能够调用适当的 PHP 函数来查看页面或在数据库中存储信息。每个挂钩都定义了一套参数和一个返回类型。挂钩的语法是在已定义接口前面加上模块的名称。例如,用来删除 announcement 类型的节点的挂钩会采用 announcement_delete(...) 形式。这个挂钩允许您的定制模块删除您的模块在数据库中创建的而且在给定节点被删除时应该同时删除的任何信息。
另一个挂钩例子是查看同类型节点的挂钩 announcement_view(...),它允许模块根据当前用户的访问特权对内容进行过滤。在页面上显示内容的一个挂钩例子是 announcement_block(...)。这允许模块返回主题化的内容的一个小容器,可以用作任何 Web 页面上的区块。
下面要简短地描述用于节点访问、数据库交互和主题化的几个基本挂钩。在下一篇文章中描述我们的定制 announcement 模块时将更详细地进行讨论。
在这一节中,我们将讨论需要为新的节点模块编写的几个典型挂钩。Drupal 的适当版本的 API 参考中提供了挂钩的所有文档。我们描述的挂钩取自我们的 announcement 模块示例,应该将它作为参考,而不是模块开发的严格方法。
_perm
perm 挂钩定义可以分配给每个用户的访问权限类型。节点模块定义了几种标准类型:
function node_perm() { return array('administer nodes', 'access content', 'view revisions', 'revert revisions'); }
节点模块对管理节点、访问内容、查看修订和恢复修订进行区分。在将访问特权分配给不同的用户角色时要使用这些字符串(见图 4)。尽可能使用现有的类型,但是如果模块的语义很特殊的话,可以创建自己的类型。例如,如果 announcement 模块需要区分创建操作和编辑操作,就可以在挂钩的数组中提供 create announcement 和 edit announcement 值。
Drupal 函数 user_access() 可以针对这些字符串类型检查当前用户的特权。如果当前用户拥有 “access content” 特权(也就是,该用户有权查看 Web 站点上的一般性内容),函数 user_access('access content') 就会返回 TRUE。模块的 menu 挂钩中常常使用这个函数,但是每当需要在执行操作前对用户的权限进行评估时都可以使用它。
_access
access 挂钩能够将对内容的访问限制为某些操作。传递节点操作(查看、创建、更新、删除等等)和节点来帮助确定访问特权。通过返回一个布尔值,可以启用或禁用节点上的某些操作。例如,您可能希望检查当前用户是否是节点的作者,以此确定该用户是否可以执行当前操作。为此,可以使用清单 2 中的代码。
function _access($op, $node) { if ($op == 'update' || $op == 'delete') { if ($user->uid == $node->uid or user_access('edit ')) { return true; } else { return false; } } }
如果发出请求的用户是内容的所有者,或者他们已经被显式地授予编辑此类型内容的权限,那么这个 access 挂钩示例允许执行 update 或 delete 操作。
_form
form 挂钩定义用来添加或编辑这个模块的节点的输入组件,比如文本域、复选框等等。文档中提供了这个函数的示例。
form 接口的实现在 Drupal 4.6 和 4.7 之间已经改变了。
_validate
在将数据提交给 Web 站点之前,模块使用validate 挂钩确保节点中的数据是有效的。
_submit
在 validate 挂钩成功完成之后,在将数据提交给 Web 站点之前,模块使用submit 挂钩对节点中的数据进行修改。
_view
view 挂钩为模块提供定义此类型节点的显示方式的机制。它首先通过使用另一个挂钩对节点进行过滤,然后通过主题化过程生成 HTML。
_menu
menu 挂钩使模块能够定义它希望处理的 URL 路径。返回值是一个条目数组。每个条目是一个关联数组,定义一个惟一的 URL。返回值有各种形式,从路径树形式的一般条目到回调条目(为某一路径注册要调用的函数)。
menu 挂钩函数的一个示例见清单 3。
function _menu() { $items = array(); $items[] = array('path' => '', 'title' => t('Module Name'), 'access' => user_access('access content'), 'type' => MENU_SUGGESTED_ITEM, 'callback' => '_page'); return $items; }
这个示例让 Drupal 能够识别 URL /,在默认的菜单列表中使用它,确保只有具有 “access content” 权限的角色才能访问它,并在这个 URL 出现在浏览器中时使用 _page 函数。
_nodeapi
如果需要与系统中的其他模块进行交互,nodeapi 挂钩就很有用了。例如,如果启用了 comment 模块,那么可以调用 comment_nodeapi() 函数,从而使用与节点相关联的评论信息来扩展节点对象。nodeapi 挂钩还可以用于其他事件,包括查看、数据库访问、搜索、检验等等。
数据库中的 node 和 node_revisions 表包含系统中存储的信息段的大多数相关信息,比如标题、摘要、主体、创建者和创建时间。这个系统可以保存节点的许多修订,而不会影响性能或可伸缩性。在编写新的节点模块时,通常要将一个或多个新表添加到数据库中。在系统中创建和修改新节点的内容时,数据库挂钩允许模块创建、修改和删除表中的数据。
Drupal 有一个数据库抽象层,其中包含几个函数,它们将高层 Drupal 数据库 API 链接到 MySQL 或 PostgreSQL 的低层 PHP 数据库模块 API。通过为数据库实现数据库抽象 API,实现了跨数据库兼容性。
但是,需要考虑一个非常重要的 问题:要想创建真正跨数据库兼容的模块,就必须非常小心地编写 SQL,这些 SQL 必须在所有数据库平台上都能够工作。Drupal 还没有提供处理这些兼容性问题的系统或机制。
_load
load 挂钩允许模块从数据库中额外的表中装载与此类型节点相关的任何额外信息。返回值是一个对象,其中包含要添加的额外属性。Drupal 会自动地将它们与正在装载的节点合并。例如,代码可能像清单 4 中这样。
function _load($node) { $additions = db_fetch_object(db_query('SELECT * FROM {} WHERE nid = %s', $node->nid)); return $additions; }
系统会自动地将 $additions 合并到装载节点的结果中。
_insert
insert 挂钩通知模块将节点的数据添加到数据库中。通常,这会调用数据库抽象层中的一个函数(例如,db_query),从而将节点的相关数据插入为这个模块定义的表中。存储在 node 数据库表中的标准数据(比如主体、创建日期等等)是自动地写入数据库的。
_update
update 挂钩使模块能够更新数据库中现有节点的数据。如果要支持模块的节点修订,update 挂钩也是最常见的实现所需代码的位置。
_delete
delete 挂钩允许模块在从数据库中删除节点时执行额外的操作。在这时,可以清空模块特有的任何表。




回页首
Drupal 使用它的菜单系统来定义 Web 站点的导航。在构建定制的模块时,可以指定模块如何根据 URL 为内容提供服务。当接收到页面请求时,系统根据路径的层次化结构寻找最接近的匹配。如果注册了一个路径,它就使用定义的回调函数来生成要显示的内容。路径的任何部分都可以用来选择如何显示内容。例如,如果路径是 /announcement/15/edit,那么将显示 id = 15 的节点的编辑页面;而对于路径 /announcement/15/view,仅仅显示节点的内容。回调函数是在_menu 挂钩中定义的。
Drupal 还有一种用来定义选项卡式界面的机制。这些选项卡是在菜单系统中作为 “本地任务(local tasks)” 指定的。在定义本地任务时,可以指定其中一个作为默认任务。默认本地任务是在用户查看内容段时首先显示的任务。我们建议在使用这个特性时总是指定一个默认本地任务。
_menu 返回一个菜单规范数组。清单 5 中的代码片段显示一个菜单规范,它为一个典型的定制模块定义了回调函数。
$items[] = array('path' => '', 'title' => t('Module Name'), 'access' => user_access('access content'), 'type' => MENU_SUGGESTED_ITEM, 'callback' => '_page');
规范中的属性包括:
Path
当这个路径与 URL 请求匹配时,就使用这个条目。
Title
菜单条目的标题。
Access
这个属性的值决定当前用户是否可以访问这个条目所指定的内容。
Type
菜单规范的类型。
Callback
调用这个函数来生成在使用这个条目时要显示的内容。
有几种不同类型的菜单规范,包括:
MENU_NORMAL_ITEM
这是菜单条目使用的默认类型,它们会显示在菜单树中。
MENU_ITEM_GROUPING
这种类型对条目进行分组,简单地列出要访问的子页面。
MENU_CALLBACK
注册一个回调函数,它会生成在访问 URL 时要显示的内容。
MENU_SUGGESTED_ITEM
管理员可以启用来自模块的建议条目。
MENU_LOCAL_TASK
这些页面显示为选项卡。其他显示方式也是可能的。
MENU_DEFAULT_LOCAL_TASK
每组本地任务还应该提供一个默认任务,它链接到与它的父条目相同的路径。




回页首
系统中的另一个高层对象是用户对象,它允许为访问站点的用户设置帐号。管理员还可以创建不同的角色,从而设置不同的内容访问特权。然后,可以将一个或多个角色分配给用户。注意,在配置 Drupal 安装时创建的第一个用户是有权修改系统中的任何设置的惟一 帐号。
管理员可以在 Drupal 管理界面的访问控制部分中为用户分配不同的角色。图 3 显示为我们的 Web 站点创建的角色。除了标准的角色之外,我们增加了管理员、客户、操作员和工作组负责人的角色。

图 4 显示管理员屏幕,在这个屏幕中可以将模块中的访问权限分配给角色。管理员使用复选框来启用或禁用特定角色对模块相关操作的访问特权。角色列在表的顶部,而按照模块分组的权限列在第一列中。权限是 _perm 挂钩中指定的字符串。将用户分类成角色就可以为不同的用户类分配任务责任。





回页首
Drupal 使用一个主题系统将内容和表示方式分隔开。可以使用 Drupal 中的各种主题引擎(theme engine) 对内容进行主题化。可以完全使用PHP 编写主题,但是主题引擎提供了一个开发主题的框架,使用它可以节省时间。当前,Drupal 站点上提供了PHPTemplate、XTemplate 和Smarty 主题引擎。我们选用 PHPTemplate 引擎,因为它是默认引擎并以一致的方式跨逻辑层、模块和表示层使用 PHP。
对于模块开发人员来说,理解核心主题代码如何搜索适当的主题方法是很重要的。编写模块的方式应该允许其他系统实现者将模块的内容结合到站点的外观和感觉中。Drupal 当前按照以下次序搜索三个构建主题化内容的 PHP 函数:
_
这个函数的名称由当前主题的名称和被主题化的内容的名称(即节点类型)组成。如果当前主题名为 ibc,而且我们要对内容 announcement 进行主题化,那么这个主题函数名就是 ibc_announcement。
_
这个函数的名称由当前主题引擎的名称和内容的名称组成。我们将使用 PHPtemplate 引擎,所以对于内容 announcement,这个主题函数名就是 phptemplate_announcement。
theme_
这是最后一个函数,也是最简单的。如果要对内容 announcement 进行主题化,那么这个主题函数名就是 theme_announcement。
使用 Drupal主题目录(themes directory) 中的文件集合来定义主题。Drupal 附带主题所需的文件,而且这些文件依赖于主题引擎。可以以许多方式对主题进行定制;我们将介绍我们如何修改数据的表示来获得 Web 站点所需的 xHTML 结构、样式和布局。
PHPTemplate 允许将特定的文件(称为模板文件)映射到 Drupal 中的特定函数和模块。模板文件以 .tpl.php 结尾,它使用相关联的函数或模块传递给它的一个数据数组。可以使用 PHP 和 xHTML 操作这一数据,将它显示在 Web 页面上。Drupal 为现有的节点类型提供了一般性模板,帮助您对主题进行定制。page.tpl.php、node.tpl.php 和 comment.tpl.php 文件是一般性模板的例子。它们保存在主题目录中。页面模板定义的页面用来包含节点模板定义的任何节点的显示。
page.tpl.php
这里很可能是开始对主题进行定制的地方。这个模板定义 Drupal 显示的所有内容页面的结构。在这里,可以定义全局的结构化 xHTML 元素(比如 head 和 body 元素)、样式表的包含文件、设置内容布局的语义的骨架 DIV 元素等。
node.tpl.php
这个模板用来控制如何显示节点数据。如果希望对特定类型的节点进行主题化,那么要复制 node.tpl.php 文件并将文件名改为 node-.tpl.php,其中的 是节点类型的名称。我们在 IBC Web 站点的讨论部分中使用一个称为 node-forum.tpl.php 的模板,从而改变论坛内容的默认布局。
comment.tpl.php
这个模板文件控制一条评论的布局。可以使用 Drupal 的Comment 模块 将评论添加到页面中。
PHPtemplate 引擎允许将模板映射到特定的主题函数(theme function)。主题函数提供生成 Web 内容的一般性方法,模块使用它们提供 Drupal 中的核心函数或者对 Drupal 进行扩展。
以 theme_links 函数为例。对于一系列 xHTML 锚元素(链接),theme_links 将返回一个包含这些链接的字符串,链接之间以给定的字符分隔。这是一个非常简单的构造块。清单 6 修改输出,用 class 属性值为 links 的 DIV 元素包围链接的列表。
在单独主题的目录中可以使用一个特殊文件,它称为 template.php。如果这个文件存在,Drupal 就使用它覆盖主题系统的默认操作。在 template.php 文件中,可以创建清单 6 所示的函数:
function phptemplate_links($links, $delimiter = ' | ') { if (!is_array($links)) { return ''; } $content = ''; return $content; }
通过在 template.php 文件中创建 phptemplate_links 函数,我们让 Drupal 覆盖默认的 theme_links 函数。在覆盖的函数 中,我们在链接列表外边加上 DIV 元素并返回产生的字符串。
如果创建新模块来扩展 Drupal 函数,就需要告诉 Drupal 您希望如何显示这个模块生成的数据。为了保持可扩展性,最好是在可由您或他人选择使用的主题化系统轻松重写的模块中设置数据的默认显示方式Drupal 主题系统给这个模板提供的一个变量。
创建默认的显示方式
为了生成默认的主题化输出,我们在模块中创建一个函数。这个函数操作传递给它的数据,并在一个字符串中返回主题化后的内容。例如,在一个称为 shopping_list_items 的模块中,对条目列表进行主题化。主题化后的输出是一个 xHTML 无序条目列表,如清单 7 所示:
function theme_shopping_list_items($list = array()) { $content = '
    '; foreach ($list as $list_item) { $content .= '
  • '.$list_item.'
  • '; } $content .= '
'; return $content; }
然后,在模块中构建 Web 页面的部分中,使用 Drupal 主题函数调用 theme_shopping_list_items 函数来返回主题化后的列表。执行的调用如清单 8 所示:
$tools = array('hammer', 'drill', 'saw'); $content .= theme('shopping_list_items', $tools);
用主题覆盖模块的输出
正如前面所解释的,可以使用主题目录中的 template.php 文件覆盖主题函数。对于上面的示例,假设我们希望将显示 shopping_list_items 模块生成的条目列表的默认操作从 xHTML 无序列表改为 xHTML 定义列表,见清单 9。我们在 template.php 文件或节点模块文件中创建这个函数。
function phptemplate_shopping_list_items($items = array()) { $content = '
'; foreach ($items as $list_item) { $content .= '
'.$list_item.'
'; } $content .= '
'; return $content; }
因为 Drupal 知道我们的主题使用 PHPtemplate 引擎,所以它会自动地用这个函数覆盖 shopping_list 模块中的 theme_shopping_list_items 函数。产生的结果是工具的 xHTML 定义列表。还可以更进一步,可以在这个覆盖函数中定义一个文件名,从而让 Drupal 使用一个模板文件定义这个列表的显示,见清单 10。
function phptemplate_shopping_list_items($items = array()) { $template_file = 'shopping_list_items'; $content = _phptemplate_callback('shopping_list_items', array('items' => $items), $template_file); return $content; }
这里的模板文件可能包含以下代码:

请记住,我们正在使用 PHPtemplate 引擎。_phptemplate_callback 函数将变量 list_items 设置为 $items 数组,并将对 phptemplate_shopping_list_items 的调用连接到模板文件 shopping_list_items.tpl.php。这种内容主题化方法是首选的,因为它干净地将大块 PHP 代码与 xHTML 分隔开了。
我们已经描述了如何使用 Drupal 的主题化系统扩展和修改内容的默认显示方式。另一个问题是如何控制每个节点生成的内容的结构和应用于此结构的样式。Web 设计人员可以利用 PHPtemplate 引擎维护模块生成的数据的结构化 xHTML。还允许使用层叠样式表(Cascading Style Sheets,CSS)修改这个结构的显示方式或样式。我们发现,最好是将数据生成放在模块中,将 xHTML 生成放在模板中,这样有助于分隔数据和显示。
Web 页面的总体结构由主题目录中的 page.tpl.php 文件控制。在这个文件中,可以使用 xHTML 定义 Web 页面的基本布局,其他节点的内容将按照这个布局包含在页面中。因为在我们的 Web 站点上页眉、主体、边栏和页脚的位置是保持不变的,所以在这里定义这些结构。
我们小心地编写导航元素、部分标题等的 xHTML 标记,使这个 Web 站点的内容在无法应用样式的情况下仍然以一种合理的格式显示。
Drupal 主题系统给这个模板提供的一个变量可以指出主页是否显示。通过使用少量 PHP,可以使用这个变量构造与 Web 站点的子页面略有不同的 xHTML。
按照前一节中介绍的方法,我们对所有节点生成的输出使用模板进行控制,因此在一个主题目录中控制内容的结构。每个节点模板的输出采用一致的结构,通常是将输出包围在一个 DIV 元素中,这个元素的 class 属性值描述使用的模板。这不但有助于对内容应用样式,还可以帮助进行调试,因为在查看页面源代码时很容易看出哪个模板生成了什么内容。
我们在 page.tpl.php 文件中定义的 Web 页面头中引用屏幕和打印媒体样式表。为了对样式进行分类和管理,我们将 CSS 样式放在单独的文件中并在主屏幕媒体样式表文件中包含它们。在后面的一篇文章中,将详细介绍对这个 Drupal Web 站点进行结构化和样式化所用的技术。
常见 xHTML 元素的基本样式包含在一个 CSS 样式表中,布局放在另一个样式表中,对 Web 站点各个部分的样式修改放在各自的文件中。与模板文件一样,所有样式表文件也放在主题目录中。




回页首
在本节中,我们讨论 Drupal 如何构建节点。开发人员需要理解 Drupal 以什么次序为特定节点收集信息,核心系统如何与模块进行交互,以及节点在 Web 页面中显示之前如何对节点进行处理。因为本节只进行概述,所以某些细节超出了本节的范围。图 5 显示了节点构建序列的流程。

查看这张图的大版本。
URL 请求中的路径形式为 /node/(即节点 类型),后面跟着节点 id,然后是节点 操作。如果路径是 /node/15/edit,这就是让 Drupal 显示节点 15 的编辑表单。这种结构惟一的例外是,当 URL 路径是 /node/add 或 /node/add/ 时,Drupal 会进行 Add 操作。
在本节中,学习以下四种操作:
View
这是默认操作,它构建只进行查看的节点页面。
Add
这种操作显示一个用来添加新节点的表单。
Edit
这种操作显示一个用来编辑现有节点的表单。
Delete
这种操作从 Drupal 系统中删除节点。
请记住,菜单系统知道应该根据给出的 URL 调用哪个函数。为了对节点构建序列进行解释,我们从菜单系统调用 node.module 中的 node_page() 函数开始讨论。
Drupal 需要做的第一件事情是判断操作。如果在 URL 中的节点 id 后面没有找到操作,就采用默认的 View 操作。
在处理任何节点的显示之前,要为与这个节点相关联的所有数据创建一个容器。用 node 和 node_revisions 表中的数据库记录数据填充这个节点对象(node object),这个记录的主键字段值与 URL 中的节点 id 相同。这些数据包含节点类型、标题、摘要、主体、创建者和创建时间。
接下来,将任何扩展数据应用于这个节点对象。这通过两个挂钩来完成,load 和 nodeapi。nodeapi 挂钩为任何模块提供了对 Drupal 核心操作(装载、查看、准备、删除)进行扩展的另一种方法。
因为 node_type 是已知的,Drupal 使用它判断 _load() 函数是否存在。例如,如果节点类型是 announcement,那么调用 announcement_load() 函数,在这里可能用公告的发布日期和过期日期扩展默认的节点信息。
现在,Drupal 调用所有可用模块中的任何 nodeapi 挂钩。这个函数调用包含一个参数,它让 Drupal 装载节点对象,并允许将来自任何模块的额外数据插入这个对象中。
然后,Drupal 存储节点的标题,在请求生命周期的后期可以将这一信息包含在 Web 页面的标题中。
Drupal 现在开始将节点对象数据放入内容中的过程。默认操作是为节点主体和摘要创建并存储主题化的输出。如果 Drupal 找到_view() 挂钩,就会调用它来覆盖这些内容片段的主题化。例如,如果节点类型是 announcement,那么除了主体和摘要内容之外,announcement_view() 函数可能还会返回公告发布日期和过期日期的主题化内容片段。
与这个序列的装载部分一样,Drupal 现在寻找所有可用模块中的任何 nodeapi 挂钩。这个函数调用包含一个参数,它让 Drupal 对节点对象进行主题化,并允许任何模块修改或扩展主题化的主体片段中的内容。然后,将修改后的内容存储在节点对象中。
链接是另一种可以添加进节点对象的内容。它们为节点内容提供额外的主题化的 Web 链接,Drupal 允许任何模块通过link 挂钩添加这些链接。
然后,Drupal 检查是否应该为这个节点显示评论。它使用节点对象中存储的任何评论数据对内容片段进行主题化,然后将结果存储回节点对象中。
最后,节点对象(包含所有数据和主题化的内容)被传递给主题化系统,从而作为主题化的节点显示。这时,节点已经完全构建好了,可以显示了。
当 URL 路径指示 Drupal 添加一个节点时,节点构建序列与 View 操作有显著差异。首先,Drupal 检查 URL 路径中是否存在节点类型。如果存在,还要检查发出这一请求的用户是否有权创建新节点。如果这些条件都得到满足,Drupal 就开始组装添加这种类型的节点所需的表单。如果路径中不存在节点类型,就显示一个列出可用节点类型的页面。
与 View 操作一样,Drupal 创建一个节点对象,其中存储用来显示表单的任何数据。为了准备这个节点对象的数据,Drupal 调用_prepare() 挂钩(如果它存在的话)。这为相关联的模块提供了对需要包含进节点对象中的任何数据进行预处理的机会。
为了让任何其他模块都能够扩展或修改节点对象,要用一个参数调用所有 nodeapi 挂钩,这个参数指示 Drupal 准备显示 add 表单。
现在,Drupal 构造出一个数据结构,这个结构描述与节点对象数据相应的表单组件。在显示表单之前,调用任何模块中的_form_alter() 挂钩,从而允许对表单数据结构进行修改。这里并不介绍 Drupal 处理表单的方式,而是留到以后的文章中讨论。
例如,taxonomy 模块可以使用它的 form_alter 挂钩在表单中插入一个字段,为这种节点类型选择分类项。
最后,使用表单数据结构将产生的节点显示在表单上。
Edit 操作与 Add 操作相似,只是在 URL 路径中提供了节点 id。
在使用 Edit 操作构建节点时,Drupal 同样要创建一个节点对象来存储显示最终页面所用的数据。与View 操作 一样,要调用 _load 和 _nodeapi 挂钩,从而允许模块扩展或修改节点对象数据。当然,如果在数据库中没有找到给定的节点,或者编辑这个节点的访问权限被禁止了,那么要通过 Drupal 消息系统在 Web 页面上给出适当的响应。
如果这个节点 id 存在而且允许进行编辑,Drupal 就会执行与Add 操作 相似的过程。但是,因为节点对象包含与现有节点相关联的数据,表单数据结构会在显示表单时在表单组件中显示这些数据。
与其他操作相比,Drupal 处理 Delete 操作的方式略有不同。这个过程也像 View 和 Edit 操作那样创建并填充节点对象,但是节点对象数据并不用来显示节点或编辑节点数据的表单。相反,要检查删除节点所需的访问权限。然后,Drupal 在 Web 页面上创建一个简单的表单,请用户确认这次删除操作。
尽管本节不描述 Drupal 的表单提交过程,但是您应该知道需要调用_delete 和 _nodeapi 挂钩,从而允许模块对与这种节点类型相关联的任何扩展数据库表或变量进行清理。




回页首
在本文中,介绍了 Drupal 中使用的概念、术语和技术。这些概念形成了 Drupal 方式的核心。您学习了:
节点
区块
模块和挂钩接口
URL 设计和菜单系统
用户和权限
对 Web 站点的外观进行主题化
节点构建序列
尽管本文只对这些概念做了简单的介绍,但是它们对于理解 Drupal 的内部工作原理是很重要的。一旦理解了这些基本知识,就很容易定制自己的环境和 Web 站点。
在本系列的下一篇文章(第 6 部分)中,将使用一个定制模块扩展 Drupal,从而为我们的 Web 站点创建公告。您将详细地了解我们的模块,包括代码示例。
学习
您可以参阅本文在 developerWorks 全球站点上的英文原文 。
进一步了解这个系列。在这个系列中,Internet Technology Group 团队描述一个需要定制 Web 站点的虚构的组织,这个 Web 站点包含文档存储、讨论组、专门的工作组、研讨会日程安排、日程议题描述等功能。
Drupal 站点提供了与本文相关的文档:Introduction to Drupal Terminology
Terminology
Node types
Hooks
Menu system
Drupal's node building mechanism
Drupal's page serving mechanism
订阅这个系列的 RSS feed。(进一步了解RSS。)
随时关注developerWorks 技术活动和网络广播。
获得产品和技术
使用IBM 试用软件 构建您的下一个开发项目,这些软件可以从 developerWorks 直接下载。
讨论
参与论坛讨论。
通过参与developerWorks blog 加入 developerWorks 社区。



Alister Lewis-Bowen 是 IBM 的 Internet Technology Group 的高级软件工程师。他从 1993 年开始作为 IBM 英国职员从事互联网和 Web 技术方面的工作。Alister 后来到美国为 IBM 赞助的体育活动的 Web 站点工作,之后成为 ibm.com 的高级网管。他当前正在帮助创建语义 Web 原型。可以通过alister@us.ibm.com 联系 Alister。



Stephen Evanchik 是 IBM 的 Internet Technology Group 的软件工程师。他是许多开放源码软件项目的代码贡献者,其中最著名的是 Linux 内核中的 IBM TrackPoint 驱动程序。Stephen 当前从事语义 Web 技术。可以通过evanchik@us.ibm.com 联系 Stephen。



Louis Weitzman 是 IBM 的 Internet Technology Group 的高级软件工程师。他从事设计和计算已经有 30 年了。他曾经帮助开发 ibm.com 使用的基于 XML 片段的内容管理系统,当前正在从事将设计过程融入新项目的工作。可以通过louisw@us.ibm.com 联系 Louis。

太差! (1)
需提高 (2)
一般;尚可 (3)
好文章 (4)
真棒!(5)
将您的建议发给我们或者通过参加讨论与其他人分享您的想法.




回页首

IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过提交转载请求表单 联系我们的编辑团队。
关于 IBM     隐私条约     联系 IBM     使用条款
使用开源软件设计、开发和部署协作型 Web 站点,第 5 部分: Drupal 入门 使用开源软件设计、开发和部署协作型 Web 站点,第 11 部分: 使用 Drupal 中... 使用开源软件设计、开发和部署协作型 Web 站点,第 6 部分: 在 Drupal 中构建... 使用开源软件设计、开发和部署协作型 Web 站点,第 13 部分: Eclipse 中的 ... 使用开源软件设计、开发和部署协作型 Web 站点,第 14 部分: announcemen... 使用开源软件设计、开发和部署协作型 Web 站点,第 10 部分: 外部网 Web 站点的... 使用开源软件设计、开发和部署协作型 Web 站点,第 12 部分: 主机托管和部署 使用开源软件设计、开发和部署协作型 Web 站点,第 12 部分: 主机托管和部署 使用开源软件设计、开发和部署协作型 Web 站点,第 8 部分: 使用 CSS 对主题化内... 使用开源软件设计、开发和部署协作型 Web 站点,第 1 部分: 简介和概述 使用开源软件设计、开发和部署协作型 Web 站点,第 4 部分: 在 Linux 中建立开... 使用开源软件设计、开发和部署协作型 Web 站点,第 2 部分: 设计有效的用户体验 使用开源软件设计、开发和部署协作型 Web 站点,第 3 部分: 在 Windows 中建... 使用开源软件设计、开发和部署协作型 Web 站点 创建新的门户: 第 5 部分:开发、构建和部署门户 .NET开发资源站点和部分优秀.NET开源项目 使用Ant进行Web开发(第二部分) 使用 Rational AppScan 保证 Web 应用的安全性,第 1 部分: Web 安全与 Rational AppScan 入门 基于服务的企业集成模式轻松入门,第 3 部分: Web services 和注册中心 基于服务的企业集成模式轻松入门,第 3 部分: Web services 和注册中心 使用 HTML 5 创建移动 Web 应用程序,第 2 部分: 使用 HTML 5 开启移动 Web 应用程序的本地存储 Symbian和C++ SDK开发入门之部署 使用 PHP 和 DHTML 设计 Web 2.0 应用程序,第 1 部分: 使用这些技术打造有特色的应用 使用 PHP 和 DHTML 设计 Web 2.0 应用程序,第 2 部分: 使用 JavaScript 创建 HTML 动态元素