Drupal专业开发指南 第17章 在Drupal中使用jQuery(3) 编写一个使用jQuery的Drupal模块

创建一个jQuery的投票小部件  译者:老葛    Eskalate科技公司

让我们编写一个基于jQueryDrupal模块。我们将建立一个如图17-2所示的Ajax的投票小组件,它可以让用户为喜欢的文章添加一分。我们使用jQuery来处理投票和总分的改变,而不用重新加载整个页面。我们还添加一个基于角色的授权,这样只有具有“rate 内容”授权的用户才允许投票。由于每个用户的每次投票只能增加一份,让我们将模块的名称命名为“plus1”。

17-2 投票组件

 

在我们接触到plus1jQuery部分以前,首先我们需要构建模块所需的基本一些代码。如果你以前从来没有创建过模块,请参看第2章。如果有经验的话,现在就开始了。

  sites/all/modules/custom下面创建一个名为plus1的目录(你可能需要创建这个目录如果它不存在的话)。在目录plus1下面,创建文件plus1.info,它包含下面的代码:

name = Plus 1

description = "A +1 voting widget for nodes. "

version = "$Name$"

该文件将模块注册到Drupal中,这样可以通过管理页面启用或者禁用它。

    接着,你将创建plus1.install文件。这个PHP文件里面的函数将在模块启用或者禁用的时候调用,一般用来创建或者删除数据库表。在这里我们想用它来追踪谁为哪个节点投了票。

<?php

// $Id$

/**

* Implementation of hook_install().

*/

function plus1_install() {

switch ($GLOBALS['db_type']) {

case 'mysql':

case 'mysqli':

db_query("CREATE TABLE {plus1_vote} (

uid int NOT NULL default '0',

nid int NOT NULL default '0',

vote tinyint NOT NULL default '0',

created int NOT NULL default '0',

PRIMARY KEY (uid,nid),

KEY score (vote),

KEY nid (nid),

KEY uid (uid)

) /*!40100 DEFAULT CHARACTER SET UTF8 */");

break;

case 'pgsql':

db_query("CREATE TABLE {plus1_vote} (

uid int NOT NULL default '0',

nid int NOT NULL default '0',

vote tinyint NOT NULL default '0',

created int NOT NULL default '0',

PRIMARY KEY (uid,nid)

);");

db_query("CREATE INDEX {plus1_vote}_score_idx ON {plus1_vote} (vote);");

db_query("CREATE INDEX {plus1_vote}_nid_idx ON {plus1_vote} (nid);");

db_query("CREATE INDEX {plus1_vote}_uid_idx ON {plus1_vote} (uid);");

break;

}

}

/**

* Implementation of hook_uninstall().

*/

function plus1_uninstall() {

db_query('DROP TABLE {plus1_vote}');

}

 

还有就是添加plus1.css文件。这个文件不是必需的,但它可以使投票组件看起来更美观,如图17-3所示。

17-3 带有CSS和不带有的对比

 

plus1.css添加如下内容:

div.plus1-widget {

width: 100px;

margin-bottom: 5px;

text-align: center;

}

div.plus1-widget .score {

padding: 10px;

border: 1px solid #999;

background-color: #eee;

font-size: 175%;

}

div.plus1-widget .vote {

padding: 1px 5px;

margin-top: 2px;

border: 1px solid #666;

background-color: #ddd;

}

现在你创建了起支撑作用的文件,现在让我们将注意力集中到jQuery的JavaScript文件,还有模块文件本身。创建两个空文件,其中一个命名为jquery.plus1.js,另一个命名为plus1.module,并将它们放到plus1文件夹下。在接下来的步骤中,你将逐步的像这两个文件添加代码。总结一下,你创建了以下文件:

sites/

all/

modules/

plus1/

jquery.plus1.js

plus1.css

plus1.info

plus1.install

plus1.module

 

创建模块

 

在一个文本编辑器中打开空的plus1.module文件,并向其中添加标准的Drupal头部文档:

<?php

// $Id$

/**

* @file

* A simple +1 voting widget.

*/

接下来你要一个一个的添加你要用到的Drupal钩子函数。其中一个比较简单的是hook_perm()的使用,它让你为Drupal的基于角色的访问控制页面添加一个“rate 内容”的授权,你将使用这一授权来阻止匿名用户在未创建帐号或者登陆的时候投票。

/**

* Implementation of hook_perm().

*/

function plus1_perm() {

return array('rate content');

}

 

现在你将开始实现一些Ajax功能。jQuery的一个重要特性提交它自己的HTTP GET 或 POST请求,这将使你将投票提交给Drupal而不用刷新整个页面。jQuery将拦截到对投票链接的点击,然后向Drupal发送一个请求,Drupal将保存投票并返回分数。jQuery将使用新的分数来更新页面上的分数。图17-4展示了我们要做的场景图。

17-4 投票更新流程的概貌图

 

一旦jQuery拦截了对投票链接的点击事件,它需要能够将URL传递给Drupal来提交投票。我们使用钩子函数hook_menu来讲由jQuery提交的投票URL映射到一个Drupal的PHP函数。该PHP函数将投票保存到数据库中,并返回一个分数给jQuery(以JSON的形式,即JavaScript Object Notation)。

/**

* Implementation of hook_menu().

*/

function plus1_menu($may_cache) {

$items = array();

if ($may_cache) {

$items[] = array(

'path' => 'plus1/vote',

'callback' => 'plus1_vote',

'type' => MENU_CALLBACK,

'access' => user_access('rate content'),

);

}

}

return $items;在前面的函数中,当路径为plus1/vote的请求进来以后,如果请求该路径的用户拥有“rate content”授权,那么将有函数plus1_vote()处理它。路径plus1/vote/3翻译为PHP函数就是调用plus1_vote(3)(参看第4章,关于Drupal的菜单/回调系统的更详细信息)。

/**

* Called by jQuery.

* This submits the vote request and returns JSON to be parsed by jQuery.

*/

function plus1_vote($nid) {

global $user;

// Authors may not vote on their own posts.

$is_author = db_result(db_query('SELECT uid FROM {node} WHERE nid = %d AND

uid = %d', $nid, $user->uid));

// Before processing the vote we check that the user is logged in,

// we have a node ID, and the user is not the author of the node.

if ($user->uid && ($nid > 0) && !$is_author) {

$vote = plus1_get_vote($nid, $user->uid);

if (!$vote) {

$values = array(

'uid' => $user->uid,

'nid' => $nid,

'vote' => 1,

);

plus1_vote_save($values);

watchdog('plus1', t('Vote by @user accepted', array('@user' => $user->name)));

$score = plus1_get_score($nid);

// This print statement will return results to jQuery's request.

print drupal_to_js(array(

'score' => $score,

'voted' => t(' You voted ')

)

);

}

}

exit();

}

上面的函数plus1_vote()保存了当前的投票并向jQuery返回信息,信息的格式是包含了新分数和字符串You voted的关联数组,该字符串用于替换投票组件下面的“Vote”文本,我们使用了t(' You voted ')而不是在jQuery中直接创建它,这样就可以将它翻译成其它语言。这个数组传给了drupal_to_js(),drupal_to_js()将PHP变量转化为JavaScript的等价形式,在这里将一个PHP关联数组转化为了一个JavaScript关联数组。Drupal将JavaScript数组序列化为JSON格式(关于JSON的更多信息,参看http://en.wikipedia.org/wiki/JSON)。在上面的代码中我们创建了几个基本函数,现在让我们创建这些函数:

/**

* Return the number of votes for a given node ID/user ID pair.

*

* @param $nid

* A node ID.

* @param $uid

* A user ID.

* @return Integer

* Number of votes the user has cast on this node.

*/

function plus1_get_vote($nid, $uid) {

return (int) db_result(db_query('SELECT vote FROM {plus1_vote} WHERE nid = %d

AND uid = %d', $nid, $uid));

}

/**

* Return the total score of a node.

*

* @param $nid

* A node ID.

* @return Integer

* The score.

*/

function plus1_get_score($nid) {

return (int) db_result(db_query('SELECT SUM(vote) FROM {plus1_vote} WHERE

nid = %d', $nid));

}

/**

* Save the vote.

*

* @param $values

* An array of the values to save to the database.

*/

function plus1_vote_save($values) {

db_query('DELETE FROM {plus1_vote} WHERE uid = %d AND nid = %d', $values['uid'],

$values['nid']);

db_query('INSERT INTO {plus1_vote} (uid, nid, vote, created) VALUES (%d, %d, %d,

%d)', $values['uid'], $values['nid'], $values['vote'], time());

}

 

 

现在,基本的getter和setter已经写好,现在让我们关注投票小部件生成代码:

/**

* Create voting widget to display on the webpage.

*/

function plus1_jquery_widget($nid) {

// Load the JavaScript and CSS files.

drupal_add_js(drupal_get_path('module', 'plus1') .'/jquery.plus1.js');

drupal_add_css(drupal_get_path('module', 'plus1') .'/plus1.css');

global $user;

$score = plus1_get_score($nid);

$is_author = db_result(db_query('SELECT uid FROM {node} WHERE nid = %d

AND uid = %d', $nid, $user->uid));

$voted = plus1_get_vote($nid, $user->uid);

return theme('plus1_widget', $nid, $score, $is_author, $voted);

}

/**

* Theme for the voting widget.

*/

function theme_plus1_widget($nid, $score, $is_author, $voted) {

$output = '<div class="plus1-widget">';

$output .= '<div class="score">';

$output .= $score;

$output .= '</div>';

$output .= '<div class="vote">';

if ($is_author) { // User is author; not allowed to vote.

$output .= t('Votes');

}

elseif ($voted) { // User already voted.

$output .= t('You voted');

}

else { // User is eligible to vote.

// The class plus1-link is what we will search for in our jQuery later.

$output .= l(t('Vote'), "plus1/vote/$nid", array('class' => 'plus1-link'));

}

$output .= '</div>';

$output .= '</div>';

return $output;

}

 

 

 

在前面的方法plus1_jquery_widget()中,我们首先加载相应的CSS和Javascript文件,然后将小部件的主体化委托给了我们自己创建的定制函数theme_plus1_widget()。记住theme('plus1_widget')实际上调用的就是theme_plus1_widget()(参看第8章关于它是如何工作的)。创建一个独立的主题函数,而不是将HTML代码放到方法plus1_jquery_widget()中,这将允许设计者在它们想改变外观时能够覆写该函数。我们的主题函数theme_plus1_widget(),为关键的HTML元素创建CSS类选择器,这使得jQuery能够非常方便的访问到它们。还有,让我们看一下链接的URL,它指向plus1/vote/$nid,其中$nid是当前已发布节点的ID。当用户点击链接时,jQuery将代替Drupal对它进行拦截并处理。我们是通过使用jQuery监控该链接上的onClick事件来完成拦截的。看一下在我们创建链接时是如何定义CSS类选择器plus1-link。看一下在我们后面的Javascript代码中选择器的使用a.plus1-link。它由<a>元素和CSS类选择器plus1-link组成。

         函数plus1_jquery_widget()生成发送给浏览器的小部件。你想让它出现在节点的查看页面中,这样当用户查看节点时,就可以对它们投票了。你能猜一下使用哪一个Drupal钩子函数实现这一点吗?他就是我们的老朋友hook_nodeapi(),它允许我们可以像创建节点一样任意的修改节点。

/**

* Implementation of hook_nodeapi().

*/

function plus1_nodeapi(&$node, $op, $teaser, $page) {

switch ($op) {

case 'view':

// Show the widget, but only if the full node is being displayed.

if (!$teaser) {

$node->content['plus1_widget'] = array(

'#value' => plus1_jquery_widget($node->nid),

'#weight' => 100,

);

}

break;

case 'delete':

db_query('DELETE FROM {plus1_vote} WHERE nid = %d', $node->nid);

break;

}

}

 

我们将重量(weight)元素的值设为一个大点的值100,这样就确保了小部件出现在节点的底部而不是顶部。我们在删除(delete)情况下,也放置了一段代码,这样当节点被删除时与其相关的投票记录也将一同被删除。

上面就是plus1.module的全部内容了,离我们整个模块的完成现在就剩下编写jquery.plus1.js了,它不足15行代码:

// $Id$

// Global killswitch: only run if we are in a supported browser.

if (Drupal.jsEnabled) {

$(document).ready(function(){

$('a.plus1-link').click(function(){

var voteSaved = function (data) {

var result = Drupal.parseJson(data);

$('div.score').fadeIn('slow').html(result['score']);

$('div.vote').html(result['voted']);

}

$.get(this.href, null, voteSaved);

return false;

});

});

}

 

你应该将你的jQuery代码封装在Drupal.jsEnabled测试中,该测试确保了当前浏览器对特定DOM方法的支持(如果不支持的话,我们的Javascript代码就不被执行。

    该Javascript代码向a.plus1-link添加了一个事件侦听器(还记不记得我们将.plus1-link定义为CSS类选择器?),这样当用户点击链接时,它将触发一个HTTP GET请求,发送到它指向的URL。当请求完成后,返回值(从Drupal中返回)作为data参数传递到匿名函数中,用该函数给变量voteSaved赋值。返回值是一个序列化为JSON格式的Javascript数组,所以你要使用Drupal.parseJson()对其反序列化。通过关联数组的键来引用数组,数组键在Drupal内部的plus1_vote()函数中指定。最后Javascript更新了分数并将文本“Vote”改为“You Voted”。为了阻止加载整个页面(因为这是Ajax要求的),我们在Javascript的jQuery函数中将返回值置为false。

 

提示: 如果你在计算机上自己动手实践,但是小部件却不能正常工作。你需要检查一下,你是不是以内容创建者的身份登录了(这是因为用户不能对自己创建的内容投票),并检查一下登录用户是否拥有权限“rate content”(“评论内容”)。一个好用的Ajax请求测试工具是名为Firbug的FireFox插件,你可以从http://getfirebug.com/下载到它。

 

 

扩展该模块的方式

对本模块的一个很好的扩展,是允许站点管理员对特定节点类型启用投票小部件。你可以使用我们在第2章节点注释模块所用到的方法来完成它。然后,你需要在hook_nodeapi('view')

内部检查给定节点类型是否启用了投票功能。

 

兼容性

jQuery的兼容性,作为jQuery的重要信息,你可以在http://docs.jquery.com找到。总之,jQuery支持下面的浏览器:

IE6.0及更高版本

Mozilla Firefox 1.5及更高版本

Apple Safari 2.0及更高版本

Opera 9.0及更高版本

 

小结

在本章,你学到了:

    什么是jQuery

    jQuery如何工作的等一般概念

    jQuery和Drupal是如何交互的,请求和数值在前后之间的传递

    如何构建一个简单的投票小部件

 

 

 

 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值