Drupal开发教程。

Related:

 

Drupal模块开发介绍

Drupal模块就是由PHP写成的包含了子过程的文件集. 因为这些模块在整个站点的上下文中运行,因此它能使用基于主引擎的所有函数,访问所有的变量和结构. 事实上,一个模块和普通的PHP文件没任何不同:它更多的是一种基于良好的设计结构和开发模型的概念. 这种类积木性质的方式很适合开源的开发模式,因此它能允许一群开发者不必冒互相干扰的风险.

这种方法会在引擎的特殊部分执行, 然后这些能够增强现有功能的代码就会被运行.那些能执行代码的特殊部位,通过定义一定的接口得到实现的被称之为“钩子(hooks)”.

钩子(hook)生效的时候就是引擎调用模块的导出函数的时候. 这可以通过遍历模块目录实现. 假定你的模块的名字是foo(例如modules/foo.module), 并且导出函数被称之为foo_bar().如果Drupal安装了一个名叫bar的钩子(hook), 那么引擎就会调用你的foo_bar()函数.

创建drupal 6.x模块

本章讲述你如何在Drupal 6.x中创建一个模块. 包含了创建.install和.info文件,以及和Drupal菜单进行交互等.

创建模块 — 基于drupal 6.x的教程

本教程描述了如何创建基于drupal 6的模块.

模块是衔接到drupal的函数的集合, 提供了比drupal基本安装更多的功能. 读完指南后, 你将能够开发出一个基本的模块,并且能用此模块作为模块开发更高级的模块和节点(node)模块.

本教程并不是能使你制作出可发行的模块. 它既不涉及缓存, 也不会详细的讨论权限和安全问题. 本教程仅仅是一个起点,通过参考别的模块和学习drupal手册, 还有如何写安全的代码和代码标准文档获取更多信息.

本教程假定你拥有:

  • 基础的PHP知识, 包括语法和一些PHP对象概念
  • 基本能够理解数据库表, 列, 记录和SQL语句
  • 已经安装好的Drupal程序
  • Drupal的管理员权限
  • web服务器权限

 

本教程假定你对Drupal模块的内部流程一无所知. 本教程对于你写基于Drupal 5或以前的模块没有任何帮助.

入门

本教程将以一个能列出最近的blog文章或论坛话题的连接列表的模块为例子. 本文将描述如何创建初始化模块文件和目录.

第一步是为你的模块起一个简短的名字. 该名字将在所有的文件和函数名中被使用, 所以必须以一个字母开始,另外按照drupal的规定, 它只能包含小写字母和下划线. 例如, 我们选择“onthisdate“作为这个名字.

注意: 此名字将在实现hook时作为模块文件的名字和方法前缀的名字.drupal将只能识别拥有该名字为前缀和钩子(hook)同名后缀的函数名.

在给定了名字“onthisdate“之后, 开始在drupal的安装路径创建一个目录:sites/all/modules/onthisdate. 你可能需要首先创建一个sites/all/modules目录.在目录sites/all/modules/onthisdate里面, 建一个PHP文件并把它保存为onthisdate.module.在drupal 6中,sites/all/modules是适合放非核心模块的地方(非核心主题包放在sites/all/themes里),因为这里是放置涉及所有站点文件的地方. 这就使得你能简单的升级核心文件而不需要变更你自己做出的修改.另外,假如你由多个站点的drupal安装, 并且,假如你需要此模块只能被一个站点使用,那么就放到sites/your-site-folder/modules里.

所有你模块中被drupal使用的函数都将被命名为{模块名}_{钩子}的形式, 钩子(hook)是预先定义好的函数后缀.drupal将会通过这些函数来获取特别的数据, 所以通过这些已经定义好的名字, drupal就能知道怎么找到.等会等我们就会讲到钩子(hook).

让drupal识别你的模块

所有的模块都必须拥有一个{模块名}.info的文件, 该文件包含了一些有关该模块的信息.

通常的格式如下:

 

; $Id$name = Module namedescription = A description of what your moduledoes.

core = 6.x

 

对于本例, 该文件将被命名为“onthisdate.info”. 没有该文件, 那么在模块列表中就不会出现该模块. 本例中,文件内容为:

 

; $Id$name = On this datedescription = A block module that lists links to content such asblog entries or forum discussions that were created one weekago.

core = 6.x

 

Info文件细节

  • name (必须的)作为模块的显示名称. 应该根据drupal 6的大小写标准: 词的首字母大写(“Example module”,而不是“example module“或者“Example Module”).
  • description (必须的)简短的单行的描述, 在模块管理页面告诉管理员该模块的用处. 注意, 过程的描述将使页面工作不正常,所以尽量简短.此处将限制为至多255字符.注意特殊字符需要使用HTML实体值, 如果仅仅是单引号, 那么只需要在外围使用双引号括起来就行了.
  • core (必须的)在版本6.x中, 假如没有显示的声明正确的版本信息, drupal核心将会拒绝启用或运行该模块..info文件必须指明那些任意导入的模块或主题包拥有和drupal核心一致的兼容性.这就是把核心信息放在.info中的含义.
  • dependencies (可选的)其它的一些可选项也有可能在.info文件中出现, 其中一个就是模块的依赖关系. 如果一个模块需要其它一些模块被启用,那么就通过类似数组的形式列出来: dependencies[] = {模块名称}
  • package (可选的)如果一个模块定义了包的名字, 那么在admin/build/modules页里, 它将和同类包名的模块显示在一个列表里.如果未指定包名, 它将会简单的被列在‘Other’里.这些是建议的包名: Audio, Bot, CCK, Chat,E-Commerce, Event, Feed Parser, Organic groups, Station, Video,Views, Voting

 

Help 钩子

通过实现drupal的hook_help(), 我们能够提供更多关于我们模块的帮助和更多信息. 因为使用了.info文件,现在此钩子变为可选的了. 尽管如此,实现它是个很好的想法. 为了实现这个钩子,我们只需要用你的模块名代替“hook“在你的模块文件里产生这样的一个函数就行了. 因此在我们的例子模块中,实现hook_help(),只需要在onthisdate.module文件里实现一个onthisdate_help()方法就ok了:

 

<?phpfunctiononthisdate_help($path,$arg) { 

}

?>

 

$path参数提供了帮助功能的上下文: 用户访问帮助所处的drupal或模块的位置. 建议通过switch语句来处理该值.这种方式在drupal代码中很常见. 这里是一个简单的实现:

 

<?phpfunctiononthisdate_help($path,$arg) { $output= ; // 声明输出变量 switch($path){

  case “admin/help#onthisdate”:

   $output = ‘<p>’. t(“Displayslinks to nodes created on this date”) .‘</p>’;

   break;

}

return$output;

} // functiononthisdate_help

?>

 

告诉drupal谁可以访问你的模块

下个我们要写的函数是权限的函数, 在drupal实现中是hook_perm(). 这里是你定义权限名的地方 —它不进行授权或阐述此权限的含义, 它只是指出针对此模块需要有哪些权限. 在先前的章节中我们实现了help钩子,这里我们通过在onthisdate.module文件里建立一个onthisdate_perm()函数来实现hook_perm().该函数将仅仅返回一些将被用到的权限名称的列表; 如果你在模块的hook_perm()实现中定义了这些权限,那么管理员将通过Administer » User management »Permissions页的设定来决定哪些角色可以访问你的模块. 这里是我们实现的例子:

 

<?phpfunctiononthisdate_perm() { returnarray(‘accessonthisdate content’);} // functiononthisdate_perm()

?>

 

如果你希望有更加精准的权限控制, 那么你可以扩展这个权限集, 例如:

 

<?phpreturn array(‘accessonthisdate content’,‘administeronthisdate’);?>

 

针对本教程, 我们只使用一个权限. 以后我们会增加其它权限.

 

你的权限值是任意选择的, 但是必须在所有已安装的模块中是唯一的. 否则, 一个事件名将会拥有两种权限. 因此,权限值应该包含了你的模块名, 因为这样可以避免和其它模块的名称冲突. 建议的针对权限的名称约定是”{动词} {模块名}”.以下面这个例子作为你开发其它模块的例子:

 

<?phpfunctionnewmodule_perm() { returnarray(‘accessnewmodule’, ‘create newmodule’, ‘administernewmodule’);

} // functionnewmodule_perm

?>

 

声明块内容

模块被用来做各种各样的事情: 一些模块产生区块(通常出现在页面左边或右边的简要内容),其它的比如产生特别的内容类型(针对整个页面), 其它例如跟踪后台信息, 还有做以上所有的事情. 你可能听说过短语“块模块”,被用来产生块内容(例如菜单模块)或者“节点模块“被用来描述产生整页内容的块(例如blog和forum模块). 在这个阶段,此模块是个“块模块”, 因为它生成一个块.

本节将定义一个块显示最近的blog和forum的帖子. 产生块的钩子被称为“hook_block”.在onthisdate.module文件中实现该钩子的函数称之为onthisdate_block().

这里是基本的格式:

 

<?phpfunctiononthisdate_block($op =‘list’, $delta = 0,$edit = array()){ // YOUR MODULE CODEHERE} // functiononthisdate_block

?>

 

块函数有三个参数:

  • $op (或者 operation)
    hook_block()函数在执行4个不同的操作时被调用, 这些操作是“list”, “view”,“save“和“configure”. 该参数告诉你的函数, 现在哪个操作被执行了. 我们将在后面讨论“list“操作,“view“操作会在下一节中描述. “configure“和“save“操作将允许你的块有一个可配置的表单和保存这些配置 —在本例中, 我们不需要使用他们.
  • $delta
    你的模块能够定义更多的块, 在“list“操作中. 因此每个块都有一个“delta“号码, 通常是个数字, 并且在其它的一些操作中,drupal会把操作传递给“delta“号码所指出的某个块中. 本例只有一个块. 核心的drupal用户模块是一个采用多块模块的例子:用户登录块, 谁是新人块, 谁在线块.
  • $edit
    只在“save“操作中被使用, 在本文中不讨论.

 

第一个操作是“list“操作, 当模块被应用的时候, 就会列出来. 这里定义了它们如何在Administer>> Site Building>> Blocks中出现(当在管理员页创建块列表的时候,块模块将会调用此函数并传递一个$op=‘list’参数).

这里是onthisdate_block()函数的实现细节(下一节将更多):

 

<?phpfunctiononthisdate_block($op =‘list’, $delta = 0,$edit = array()){
if ($op== “list”){
  //在admin/block页里产生块列表
  $block = array();
  $block[0][“info”]= t(‘On ThisDate’);
  return $block;
}
} // functiononthisdate_block
?>

 

好, 现在我们来讨论这些代码…

 

$block – $block就是在返回它之前存储一些必须数据的变量
$block[0] – $block是一个数组类型变量, 每个元素都表示了你所提供的一个独立的块(我们的例子里只提供了一个块).“0“的数组索引是$delta值, 该值会在后面的操作中用到(如果我们定义多个块, 我们就需要$block[0],$block[1], $block[2]等; 我们也需要检查该值为了后面的操作).
$block[0][“info”] – 关键字有可能是“info”, “cache”, “weight”, “status”.那个可能需要额外花一节课事件进行介绍, 所以我们只介绍“info”, 该值的功能是在管理员菜单中给我们的块起个人性化的名字. 因此,在hook_block()中, 我们知道它的delta值是0, 但是管理员知道它叫“On This Date”.

下一节将产生块内容, 通过“view“操作实现.

产生块内容

下一步就是产生块的内容了. 这将涉及访问drupal数据库的概念.我们的目标是获取一个一周内的内容列表(作为节点存在放数据库中). 具体地说, 我们需要午夜到一周前的11:59pm内容.当一个节点初次创建, 产生的时间就将被存放到数据里.

为了告诉drupal我们所放在block中的内容, 我们使用hook_block()的‘view’操作符. 因此,我们需要在我们先前定义好的onthisdate_block()中增加更多的代码.

首先, 我们需要计算一周前午夜的时间和一周前11:59pm时的时间. 这部分代码是独立的.

 

<?phpfunctiononthisdate_block($op=‘list’,$delta=0){ if($op ==“list”) {

  // Generate listing of blocksfrom this module, for the admin/block page

  $block = array();

  $block[0][“info”]= t(‘On ThisDate’);

  return $block;

}

elseif ($op== ‘view’){

  // Generate our blockcontent

 

  // Get today’sdate

  $today = getdate();

 

  // calculate midnight one weekago

  $start_time = mktime(0,0, 0,

    $today[‘mon’],($today[‘mday’]- 7),$today[‘year’]);

 

  $end_time = time();

 

 // more coming…

}

}

?>

 

下一步就是从数据库中获取我们需要内容的SQL语句了. 我们将检索node表的内容, 此表是drupal的核心表.我们能通过这个查询获取所有类型的内容: blog内容, 论坛帖子等. 对于本文, 这已经足够了. 对于一个真实的模块,你可能需要调整SQL语句来指定需要获取的特殊内容(通过增加type列和WHERE子句来检索类型信息).

 

drupal使用数据库帮助函数来执行数据库查询. 那个意思是说, 大部分情况,你可以写你的SQL语句而不需要关心数据库的连接情况. 因此我们使用db_query()来得到记录(例如: 数据库行):

 

<?php $query= “SELECT nid, title, created FROM “.   “{node} WHEREcreated >= ‘%d‘ “.

  ” AND created<= ‘%d‘”;

 

$query_result =  db_query($query,$start_time, $end_time);

?>

 

上面描述了如何在drupal中进行安全查询: 通过使用“占位符“例如%d和%s来创建查询,然后把变量传递给db_query()来填充那些“占位符”. 这种方式能够阻止SQL注入攻击, 特别是在查询中使用用户产生的内容的时候.另外一个值得注意的地方是这里进行查询的drupal数据库表名被大括号包围, 为{node}.这是必须的,这样你的模块就能支持表名前缀. 最后, 我们还应该注意到我们并不关心访问nodes的权限.通常情况下所有的基于nodes的查询都需要使用db_rewrite_sql()函数,该函数确保浏览某页的用户有权看到此node的返回信息, 但是这些超出了本文的内容.

 

现在, 我们使用db_fetch_object()来查看通过上面的查询返回的单独的记录. 对于我们所找到的单个节点,我们生成一个包含节点标题指向该节点的连接:

 

<?php // content variable that willbe returned for display   $block_content = ;

while($links = db_fetch_object($query_result)){

  $block_content .=  l($links->title,‘node/’. $links->nid).‘<br/>

; }

?>

 

注意,事实上的连接是由l()函数产生的. l 能够根据配置clean urls或者不是,能够产生合适的<a href=“link”>连接.

 

最终, 为了显示, 我们需要返回我们产生的内容:

 

<?php // check to see if there wasany content before returning //  the blockview

if($block_content == ){  

  //no content from a week ago

  $block[‘subject’]= ‘On This Date’;

  $block[‘content’]= ‘Sorry No Content’;

  return$block;

}

 

// set up theblock

$block= array();

$block[‘subject’] =‘On This Date’;

$block[‘content’] =$block_content;

return$block;

?>

 

我们返回一个drupal期望从块函数中返回的有‘subject’和‘content’元素的数组. 如果你不包含上面这些,那么该块就可能输出不正常.

 

上面的例子假定当一星期前的日期没有内容输出的时候, 你希望块显示“Sorry No Content”.你也可以简单地选择在没有结果时不显示该块. 要这么做, 你可以:

 

  if ($block_content == ){
  return; } 

 

你可以能注意到这里把层和内容混合在一起是种不好的编程方式. 如果你期望写一个别人能使用的模块,那么你就需要提供一个更容易调整内容的层结构的方法(特别是对于非程序员). 一个更容易的方式是给你的连接加一个属性,或者把产生的html代码包围在基于模块级的css的<div>标签内而去掉<br/>. 更好的方式是使你的模块“主题化”. 现在我们可以暂时乎略它.

 

安装, 测试和启用模块

此时, 你可以安装你的模块, 它应该能正常工作

INSTALL

把模块内容拷贝到合适的地方就是安装的整个步骤.

启用

以站点管理员身份登录, 到模块管理页面,

评论

(required. But it will not be published)