这本指南适用于Drupal 5以及更早的版本,对于drupal,有一个新的主题开发指南已经可用。我们手册的这一部分,主要是关于主题系统的各个方面,希望这对广大的drupal主题开发者能够有所帮助。
强调一点 – 当使用这里所讲的任何方法开发一个主题时,你必须保证主题的名称,与你drupal站点上任何模块名称不能重名,如果重名的话,就有可能造成函数重名,进而你的站点将不能工作。
Javascript代码片断(用于主题化的)可参看Javascript部分
PHP主题化代码片断可参看 超越基本-代码片断(Beyond the basics - Snippets)
也可参看超越基本-主题HowTos(Beyond the basics - Theme HowTos)。
一小部分手册页面被移到了新的主题化手册中了.我们为Drupal6以及以后版本提供了一个全新的手册.这个手册,当它完全过时后,最终将被作为新大纲中的子部分.主题升级指南也已经被移动了.
注意:本手册是从主题开发者的角度来描述drupal主题系统的。如果你是一个模块开发者,想将你的模块主题化时,你可以参看这个页面。
从drupal4.5开始,Drupal的主题系统就非常灵活了。此时便采用了新的结构---模板引擎,模板,样式表和PHP所构成;通过插入一些组件,可以很容易的形成你自己的主题。在Drupal 4.7中drupal自带的默认主题引擎改为了PHPTemplate。
下面是一些主题的构成结构:
主题 |
引擎(PHP) |
模板(XHTML) |
样式(CSS) |
|
|
|
|
|
PHPTemplate |
.tpl.php |
.css |
Minelli |
.css |
||
Bluemarine |
.tpl.php |
.css |
|
Chameleon |
Chameleon.theme |
.css |
|
Marvin |
.css |
“主题”是一个抽象的东西,有多种方式可以生成主题:
前面例子的目录结构如下所示:
themes/engines/phptemplate/phptemplate.engine
themes/garland/page.tpl.php
themes/garland/style.css
themes/garland/minelli/style.css
themes/bluemarine/page.tpl.php
themes/bluemarine/style.css
themes/chameleon/chameleon.theme
themes/chameleon/style.css
themes/chameleon/marvin/style.css
主题和模板放在themes目录(sites/all/themes第3方主题)的子目录中。主题引擎将会在所有的子目录中寻找模板文件(.tpl.php, .xtmpl)。如果主题目录下面存在一个style.css文件的话,它也将被使用。
你也可以仅仅使用CSS,在现有主题的基础上创建一个新的(子)主题,你需要将其放在原主题的子目录下面,在这个子目录下放置一个新的style.css文件。Drupal可以将新的样式,与原主题的模板联合起来,从而形成一个新的主题。这种主题的例子有Minelli 和Marvin。
最后,如果在主题目录下面有一个screenshot.png文件的话,Drupal将在主题管理界面中使用这个截图。
创建自定义主题
如果你想创建一个自定义主题的话,你可以选择修改一个已有主题,或者从头开始。
为了修改一个已有主题,将它拷贝到你的themes目录下,给它起个独一无二的名称。在drupal中,主题名不能与drupal的默认模块名,以及启用的第3方模块名重名。接着根据你的需要来修改这份拷贝。根据主题是基于模板的,还是给予.theme文件的,你可以选择使用PHP 或者XHTML/CSS来修改它。就像前面所讲的那样,如果你仅仅想修改一个主题的样式的话,你可以在该主题下面创建一个子目录,将自己的样式style.css放进去,这样在drupal中就形成了一个新的主题。
如果你想另起炉灶的话,那么也有多种方式可供选择。如果你不是一个程序员的话,那么最简单的方案就是使用一个模板引擎。Drupal 4.6及以前版本中,默认自带的主题引擎为XTemplate,它需要你使用特殊的标记创建一个(X)HTML骨架。更多信息可参看XTemplate文档。在Drupal4.7中,PHPTemplate成为了默认主题引擎,而XTemplate成为了一个第3方的主题引擎。
在drupal主题中,可以直接使用PHP代码。这种方法仍然可用,但与给予模板的主题相比,它更难用,维护起来也更加麻烦。
相关联接: http://drupal.org/node/11774 ,
从Drupal4.7开始, Drupal主题制作者就可以定义任意数量的区域('regions')了,用于内容的展示。这个改进的区域机制,取代了原有的左栏、右栏区域,这使得drupal主题的布局和设计都更加灵活了。
对于那些不是基于主题引擎的主题,可以在.theme文件中使用themename_regions()函数来定义所用的区域。基于引擎的主题,其引擎为其定义了默认的可用区域(定义在.engine文件中),不过它们也可以定义自己的区域。
内容是通过区块系统指定到区域中去的,当然也可以使用drupal_set_content()函数。例如,drupal_set_content('left', 'Hello there'),将把文本“Hello there”指定到左栏区域中。由当前主题或者其引擎定义的所有的区域,在区块配置页面都是可用的。
由主题的.theme文件(或者主题引擎的.engine文件)定义的第一个区域,将成为主题的默认区域,例如,用在区块配置页面中,它将作为区块位置的默认选项。
对于最低限度的升级要求,可参看升级4.6主题;关于实现区域的更多细节可参看纯PHP主题和PHPTemplate页面。
PHPTemplate是由Adrian Rossouw编写的一个drupal主题引擎,Adrian Rossouw还是Drupal 4.5主题改革的幕后推动者。
它使用something.tpl.php模板文件来实现Drupal的theme_something()函数的功能。Drupal的主题化函数文档可参看Development Plumbing站点。每个模板文件都包含了一个HTML骨架,里面嵌入了一些简单的PHP语句,用来输出动态数据。因此,如果你仅仅了解一点PHP的话,那么PHPTemplate应该是你的很好的选择:使用一些基本的PHP代码片断,你可以很容易的创建高级的drupal主题。
如果你不了解PHP的话,那么PHPTemplate仍然是一个很好的选择,因为这里面仅涉及到了很少的代码,而且这些代码都是一个模式的。你可以复制粘贴这些代码就可以了。
一个扩展的论坛讨论提供了创建PHPTemplate背后的原因。
提示:输出可用的变量
输出变量数组
<?php
print '<pre>';
print_r(get_defined_vars());
print '</pre>';
?>
使用HTML标记输出变量数组
<?php
print '<pre>';
print htmlspecialchars(print_r(get_defined_vars(), TRUE), ENT_QUOTES);
print '</pre>';
?>
为了创建一个新的PHPTemplate主题,你需要在你的themes目录下,创建一个新的目录,例如themes/mytheme.接着,你需要在该目录下创建一个名为page.tpl.php的文件.或者你也可以拷贝一个已有主题,仅仅改一下名字.对于Drupal 5,你可以基于Zen 或者Blue breeze来构建自己的主题.
page.tpl.php是唯一的一个必须的文件.它将覆写theme('page')函数,用来输出页面的最终内容,包括各种修饰,比如页首、标签、面包屑、左右栏、和页脚等等。
You can create files to override the following functions:
你可以创建模板文件来覆写下面的函数
Themes/engines/phptemplate下面包含了这些模板文件的样板,对于page.tpl.php的例子可参看box_grey。简单的将这些模板文件拷贝到你的theme/mytheme目录下,然后编辑它们。注意,对于PHPTemplate主题,你需要通过访问administer > themes,来刷新它的缓存,从而识别出新的.tpl.php模板文件。
如果你要覆写的drupal主题函数,在这里没有列出来的话,你需要自己提供一个覆写。
用于输出区块中(页面的左右栏)的内容.这个模板是可选的,通过拷贝默认的模板文件,并修改它,就可以覆写这个模板.
模板可用的变量
$block对象包括:
$block->module
生成该区块的模块的名称.
$block->delta
区块在其模块中的id.
$block->subject
区块标题.
$block->content
区块的html内容
$block->status
区块的状态(0或者1).
$block->region
区域名称,默认可用的区域有'left', 'right', 'header' 和'footer'(左栏,右栏,页首,页脚).
$block->throttle:
节流阀设置.
其它变量:
$directory
主题所在的目录,比如themes/garland 或者themes/garland/minelli.
$is_front
如果当前页面为drupal站点首页的话,返回True。
$id
展示的区块的序列id,比如,第一个区块为1,第2个区块为2,等等。
$block_id
与$id一样,但是在左右栏中将被重置。
$zebra
'odd' 或者'even'。这对于使用css创建斑马线非常有用。
$block_zebra
与$zebra一样,但是在左右栏中将被重置。
默认模板
默认的block.tpl.php,位于themes/engines/phptemplate/block.tpl.php。
<div id="block-<?php print $block->module .'-'. $block->delta; ?>" class="block block-<?php print $block->module ?>">
<?php if ($block->subject): ?>
<h2><?php print $block->subject ?></h2>
<?php endif;?>
<div class="content"><?php print $block->content ?></div>
</div>
Drupal 4.6 vs. Drupal 4.7及更高版本
在Drupal 4.6中这些变量是不同的:
在drupal4.7及更高的版本中,除了默认的'left', 'right', 'header' 和'footer'以外,你还可以定义自己的区域。
推荐阅读:
我为客户创建过一些drupal站点,站点创建后由客户来维护,我发现客户对于节点、页面、区块这些drupal概念非常困惑,所以我在所有的自定义区块的底部都添加了一个链接“编辑这个区块”,而只有具有区块管理权限的用户才可以看到这个链接(同样,我对page.tpl.php也作了相应的修改,使得只有具有页面管理权限的用户才能看到页面右下角的编辑链接)。
<div class="<?php print "block block-$block->module" ?>" id="<?php print "block-$block->module-$block->delta"; ?>">
<?php print $block->subject ?>
<div class="content"><?php print $block->content ?>
<?php if ($block->module == "block"):?>
<?php if (user_access('administer blocks')) :?>
<br /><center><a href='/admin/block/edit/<?php print $block->delta;?>'>(edit this block)</a></center>
<?php endif; ?>
<?php endif; ?>
</div>
</div>
注意编辑链接的路径在各个drupal版本下有所不同。
在Drupal5.0中,设计者可以基于特定的区块、区块所属的模块、以及区块所在的区域,为区块创建多个tpl.php文件。
drupal模板文件的查找顺序如下:
例如,用户登录区块的delta为0。假如你将它放在了左栏中,当它显示时,PHPTemplate将按照下面的顺序寻找模板文件:
通过查看页面的源文件,你可以找到区块所属的模块以及delta:每个区块的主DIV都使用了下面的类和ID:
<div class="block block-{module}" id="block-{module}-{delta}">
使用CSS自定义样式
你也可以使用这些类和IDs为区块添加CSS规则,可用于所有的区块,特定模块的区块,或者单个区块。(为了通过区域来自定义区块的样式,你需要在选择器中使用整个区域DIV的类或者ID)。
注意,delta一般是一个数字:模块中区块的排序,从0开始。在一些情况下,delta也可以是一个名字:为了给Views模块创建的区块定义外观,你可以使用block-views-[name of your block].tpl.php。
注意:在Drupal 5中,这更简单了。只需要像前文所讲的那样就可以了。
方法1
这里所讲的具有一些技巧性,它允许你为特定的区块创建一个单独的block.tpl.php模板。你可以使用区块名称或者区块ID进行控制。下面是一个修改后的block.tpl.php,在前面带了一个条件语句。你所要做的就是编辑module == '[模块名]' 和delta == '[区块名或id]'部分就可以了。
下面是我当前的block.tpl.php,条件里面的区块信息是我站点特有的。如果你想在你的drupal站点上使用它的话,你需要按照前面所说的对其进行修改。这个是在我的站点上,为两个区块加载一个自定义block.tpl.php,你可以根据需要用于,1个,6个等等。对于每个自定义模板,你都需要一个条件语句("if "部分)。
<?php
if ( ( $block->module == 'views' && $block->delta == 'Cool Block' ) || ( $block->module == 'node' && $block->delta == '0' ) ) {
include 'block-custom.tpl.php'; /*load a custom template for those two block IDs */
return; }
?>
<div class="<?php print "block block-$block->module" ?>" id="<?php print "block-$block->module-$block->delta"; ?>">
<div class="title"><h3><?php print $block->subject ?></h3></div>
<div class="content"><?php print $block->content ?></div>
</div>
你需要自己创建一个block-custom.tpl.php,根据需要你可以创建任意多个这样的模板文件。为了在你的Drupal站点上进行测试,你可以复制一份标准的区块模板,将其改名为block-custom.tpl.php,然后对其进行编辑,比如将标题放在最后面而不是最前面,然后查看效果。剩下的就是你的事儿了。你可以在模板中根据需要放置任何想要的东西。
这意味着所有的区块都可以拥有自定义的外观,这是很多人都热切希望的。将这个添加到新的区域功能中,你将拥有100%的自定义区块。
方法2
JohnAlbin写的
block.tpl.php的内容:
<?php
$block_module = $block->module;
// force delta to be alphanumeric
$block_name = $block_module . '-' . preg_replace('/[^\w]/', '', $block->delta);
switch ($block_name) {
case 'menu-2': // Primary links
case 'views-CoolBlock': // a custom Views block named "Cool Block"
if ( @include("block-$block_name.tpl.php") ) {
return;
}
break;
}
?>
<div class="block block-<?php print $block_module; ?>" id="block-<?php print $block_name; ?>">
<h2 class="title"><?php print $block->subject; ?></h2>
<div class="content"><?php print $block->content; ?></div>
</div>
这段代码就有下面的优势:
1. 在id属性中不会有奇怪的字符(比如空格)。
2. 如果没有"block-[module]-[delta].tpl.php"文件的话,也不会引起PHP警告,此时将会使用默认的block.tpl.php代码。
方法3
dman写的
在block.tpl.php的最顶部:
<?php
// allow for unique blocks to have their own phptemplate theme
if(!empty($block->subject))
$blockid = 'block-'.strtolower(preg_replace("/[^A-Za-z0-9]+/","-",$block->subject));
else
$blockid = 'block-'.$block->module.'-'.$block->delta;
if (file_exists(path_to_theme() . "/$blockid.tpl.php")) {
include path_to_theme() . "/$blockid.tpl.php";
return;
}
?>
现在你就可以实现block-block-7.tpl.php或者block-login.tpl.php等等模板文件了。
在一个页面元素周围输出一个简单的html盒子(box).唯一常用的例子是,在drupal的内核中的搜索结果和评论表单中用到了.在评论试图选项周围,就是用的box.tpl.php.
注意,这个模板很少用到,我专职的做过1年的drupal开发,从没有遇到过要修改这个模板的,它仅仅用于Drupal的内核中,除非你需要开发内核,否则你是不会需要修改这个模板的.
可用变量
$title
盒子的标题.
$content
盒子的内容.
$region
区域. Main(主), left(左) 或者 right(右).
默认模板
<div class="box">
<h2><?php print $title ?></h2>
<div class="content"><?php print $content ?></div>
</div>
定义一个评论区块所输出的HTML.没有对评论进行任何逻辑处理,仅仅用于输出实际的评论.
可用变量
$author
链接到作者的个人信息页面.
$comment (object)
评论对象,将传递给theme_comment函数.
$content
链接的内容.
$date
格式化的发布日期.
$directory
主题所在的目录,比如themes/garland or themes/garland/minelli.
$id
被展示评论的序列ID.
$is_front
如果当前页面为首页的话,返回true.
$links
位于评论下面的上下文链接.
$new
英文'new'的翻译文本,如果评论确实是新的,未读过的.
$picture
用户图片的HTML(包含<a>标签),如果展示被启用并且图片被设置了.
$submitted
翻译的发布信息字符串.
$title
链接到评论的标题上.
$zebra
斑马线,odd/even,二选一.
默认Drupal模板文件
<div class="comment<?php print ($comment->new) ? ' comment-new' : ''; print ($comment->status == COMMENT_NOT_PUBLISHED) ? ' comment-unpublished' : ''; ?> clear-block">
<?php print $picture ?>
<?php if ($comment->new) : ?>
<a id="new"></a>
<span class="new"><?php print $new ?></span>
<?php endif; ?>
<h3><?php print $title ?></h3>
<div class="submitted">
<?php print $submitted ?>
</div>
<div class="content">
<?php print $content ?>
</div>
<?php print $links ?>
</div>
这个模板文件控制着节点的输出,和节点摘要的输出.它仅能影响page.tpl.php中的$content变量。
可用变量
$content
节点内容,如果是摘要的话,就是节点的teaser。
$date
格式化的节点创建日期。
$directory
主题所在的目录,比如themes/garland or themes/garland/minelli。
$id
被展示节点,在列表中的序列ID。
$is_front
如果当前页面为首页的话,返回true。
$links
节点链接。
$main (drupal4.6)
如果节点出现在上下文中,比如首页,在这里只展示teaser,此时为true。在drupal4.7及以后版本中,不再使用这个变量了。
$name
格式化的作者名。
$node (object)
节点对象。为了查看当前节点对象的所有属性,将下面的代码放到你的node.tpl.php中:
<pre><?php print_r($node); ?></pre>
$node_url
指向节点的链接。
$page
如果节点单独展示在一个页面时,返回true。
$picture
如果启用的话,返回用户图片的HTML。
$sticky
如果节点是sticky(粘的)的话,返回true.
$submitted
如果为这个节点类型启用了节点信息的展示的话,它将包含作者和节点创建日期等信息。
$taxonomy (array)
一个展示分类术语的HTML链接数组。
$teaser
布尔值,用来指示是否需要返回teaser,而不是整个节点文本。
$terms
分类术语的HTML。
$title
节点标题
$zebra
斑马线,odd/even二选一。
提示:输出可用的变量。
输出变量数组:
<?php
print '<pre>';
print_r(get_defined_vars());
print '</pre>';
?>
输出带有HTML的变量数组:
<?php
print '<pre>';
print htmlspecialchars(print_r(get_defined_vars(), TRUE), ENT_QUOTES);
print '</pre>';
?>
Drupal5.x的默认模板文件
<div id="node-<?php print $node->nid; ?>" class="node<?php if ($sticky) { print ' sticky'; } ?><?php if (!$status) { print ' node-unpublished'; } ?> clear-block">
<?php print $picture ?>
<?php if ($page == 0): ?>
<h2><a href="<?php print $node_url ?>" title="<?php print $title ?>"><?php print $title ?></a></h2>
<?php endif; ?>
<div class="meta">
<?php if ($submitted): ?>
<span class="submitted"><?php print $submitted ?></span>
<?php endif; ?>
<?php if ($terms): ?>
<span class="terms"><?php print $terms ?></span>
<?php endif;?>
</div>
<div class="content">
<?php print $content ?>
</div>
<?php
if ($links) {
print $links;
}
?>
</div>
Drupal4.7的默认模板文件
<div class="node<?php if ($sticky) { print " sticky"; } ?><?php if (!$status) { print " node-unpublished"; } ?>">
<?php if ($page == 0): ?>
<h2><a href="<?php print $node_url ?>" title="<?php print $title ?>"><?php print $title ?></a></h2>
<?php endif; ?>
<?php print $picture ?>
<div class="info"><?php print $submitted ?><span class="terms"><?php print $terms ?></span></div>
<div class="content">
<?php print $content ?>
</div>
<?php if ($links): ?>
<?php if ($picture): ?>
<br class='clear' />
<?php endif; ?>
<div class="links"><?php print $links ?></div>
<?php endif; ?>
</div>
如果你打算使用$is_front来检查当前页面是不是展示的单个节点的话,这里有篇好的帖子(尤其是帖子下面的评论更值得一读),讨论了变量$page和$is_front的不同.
只有当你处在drupal站点的首页时,变量$is_front才被设置,也就是为真,而当你处在其它页面时,$is_front为假。对于普通的节点列表页面,比如当你点击一个drupal分类术语后所得到的节点列表页面,在这里$is_front就为假(false)。
对于所有的节点列表页面,包括特定的分类术语下面的节点列表页面,$page都等于0。
如果你想为一个drupal节点使用一个特定的node.tpl.php模板文件的话,你可以将下面的代码拷贝或者合并到你的template.php中:
<?php
function _phptemplate_variables($hook, $vars = array()) {
switch ($hook) {
case 'node':
$vars['template_files'] = array('node-'. $vars['nid']);
break;
}
return $vars;
}
?>
现在,比如对于节点34,你就可以专门为它创建一个主题,名为node-34.tpl.php的模板文件.
本文是基于我的实践经验所写,我的客户向我提出了一个要求,如何让CCK定义的节点类型拥有一个他想要的主题外观。当然,有多种方式可以实现这一点:1)仅用CSS(并不是所有情况下都有效);2)使用Contemplate模块,(是不是的会让我挠头);3)一个自定义模块(根据他的情况,也不是一个好办法);4)主题模板。
随着他的需求的日益明确,我觉得主题模板模板应该是最好的办法。当然,这也是能够传授给他的最简单的方法。
具体要求和环境
新的节点类型已经用CCK定义好了,当然它还可以更精简一些。节点类型的名字是“agency”。
这个Drupal站点使用的主题为Garland。
站长对于外观没有太明确的要求,她只是想让我给他一个简单的教程,这样将来他好自己修改。他熟悉CSS,也了解一点PHP。
解决方案
首先,让我们看一下由CCK定义的节点类型。
在这个图片中,我们看到"Name"(名称)和 "Type"(类型);注意这些字段下面的说明:
为了构建主题模板文件,我们需要"Type"。
我们还需要注意,在这里我们把"body"字段叫作了什么。我们称它为"Description"。
当我们来到"Manage fields"(管理字段集)页面时,我们看到上图所示的页面。其中,"Name"列是我们需要的。然而,这里需要注意一点:我们这里没有字段叫作"body filter",这是Drupal内核中的一个表单技术,将“body”名称为"body_filter".
好了,现在全部信息我们都有了,让我们开始为这个节点类新定制主题。
打开你的文本编辑器,新建一个文件。在这里,我们将使用HTML 和PHP。首先,先输入一些基本的东西,以供将来定义CSS时使用。
<div class="content agency">
</div>
<div class="clear-block clear"></div>
它的意思是说,“下面的是这个节点的内容,节点类型为'agency'”。这里我选择使用了机器可读的节点类型作为内容标识,你也可以使用自己的标识。"content"是Drupal的标准用法,所以你需要保留它。第2行仅仅是一个结束标签。第3行也不是必需的,但是由于我们的节点中包含了图片,图片的大小可能会有所不同,加上这一行,能让浏览器返回“起点处”,从而保持统一的外观。
那么,现在我假设你想输出一些实际的内容?
节点中包含了一个图片,这里我们用的是使用了ImageCache模块的CCK "Image"字段。我们想把图片放在左上方。实际上,如果把公司名(标题)放在图片上方的话,效果可能会好一些,至于标题和图片的位置问题,我们现在先不管它,首先要做的是将它们显示出来。我们将这些东西插入到第一个<div>标签中。
ImageCache模块提供了一个非常方便的主题函数,让我们来使用它。
[顺便说一句,'echo'函数比'print'函数稍微快一点,所以我使用'echo'。不过大多数主题制作者都使用'print'。两者都能工作。
]
<?php echo theme('imagecache', 'thumbnail', $node->field_logo[0]['filepath'], $node->title, $node->title, array('align' => 'left', 'hspace' => '10')); ?>
在这个函数中,首先使用"thumbnail"(可以改为你自己要用的)来设置图片的大小。对于数据字段("$node->field_logo[0]['filepath']"),我将在下面作出解释。接下来的两个"$node->title"参数,它是告诉函数,对于"alt" 和"title"属性,在这里都使用公司名称。最后,我们使用了一个数组,它告诉ImageCache向IMG标签中添加属性'align="left"' 和'hspace="10"'。
好了,我们把图片插进去了,现在轮到标题了。在Drupal中,最标准的做法是使用<h2>标签。这个取决于你的习惯,不过在这里我将遵循Drupal标准。整个节点对象在主题中都是可用的,按照Drupal标准的,它叫作"$node"。在节点对象内部的标题(在这种情况下,就是公司名称),它的叫法就有点奇怪了,叫做 "title",而不是“company_name”。因此我们可以使用"$node->title"来引用它。
<h2><?php echo l($node->title, 'node/'. $node->nid, array('title' => t('View agency')));?></h2>
另一个标准的Drupal做法是,在标题上加一个指向节点本身的链接,这样你就可以进入节点页面,如果有权限的话,可对其进行编辑。所以我在这里使用l()函数。关于这个函数的更多信息,可参看这里。
在我的例子中,我使用表格(table)来对数据进行格式化。你也可以这样做,或者你也可以使用<div>+CSS。有些主题制作者就喜欢使用后者。你可以自由的选用你喜欢的技术。但一定要小心,不要与你的主题冲突了。
在我的例子中,我使用了CCK_Address模块,由于它与其它的CCK字段有点区别,所以我将讲解一下它的字段是怎么命名的。地址模块允许使用多个地址;多个地址构成一个名为"field_address"的数组,所以第一个地址是0,第2个地址是1,以此类推。这个模块还提供了几个子字段,它们的标签不需要与编辑页面的保持一致。比如,显示的"Address"字段,在内部为"street1";"Address continued"在内部为"street2";而"Apt/suite number"则简记为"apt."
我没有能够为这些字段使用"content_format"函数来格式化它们的内容。不知这是好是坏,但是它能工作。而普通的CCK字段有一个安全的"view"元素可用。
<tr><th>Address</th><td>
<?php
echo $node->field_address[0]['street1'];
if ($node->field_address[0]['apt']) { echo '; '. $node->field_address[0]['apt']; }
if ($node->field_address[0]['street2']) { echo '<br/>'. $node->field_address[0]['street2']; }
echo '<br/>'. $node->field_address[0]['city'] .', '. $node->field_address[0]['state'] .' '. $node->field_address[0]['zip'];
if ($node->field_address[0]['country'] != 'US') { echo '<br/><big>'. $node->field_address[0]['country'] .'</big>'; }
?>
</td></tr>
注意,我这里加了一点逻辑,从而将城市,州,和邮政编码连在一起(采用美国的格式)。由于我就在美国,所以也没有显示"US",如果加上这个的话,这里的邮递员会觉得别扭的。
现在,除了"body"字段(也就是这里的"description")以外,到目前为止,所遇到的所有字段的处理方式都是一样的。如果你参考一下你的“管理字段集”("manage fields")页面的字段名称,现在仅有几点需要你了解。
和前面的"address"一样,CCK所有字段可以是单独的,也可以是多个的,所以需要使用数足来管理它们,数组名就为“管理字段集”页面的字段名。例如,我们把电话号码定义为"field_agency_telephone",这个和前面的一样,第一个元素为field_agency_telephone[0],第2个元素为field_agency_telephone[1],以此类推。在我们这种情况下,不需要多个电话号码,如果需要的话,使用一个简单的"foreach"循环就可以做到了。
CCK提供了一个内容格式化工具(content formatter),但是你用不到它。每一个字段数组,都有一个‘view’,这个就是内容格式化工具生成的,但它已经为你做好了。这是展示数据的安全方式。而对于电子邮件地址和超链接等字段内容,CCK已经对其进行了正确的格式化处理。
让我们归者一下,引用一个字段的最好的方式就是"$field_name[0]['view'],"其中field_name可从“管理字段集”页面找到。
<tr><th>Phone</th><td><?php echo $node->field_phone[0]['view'];?></td></tr>
<tr><th>Email</th><td><?php echo $node->field_email[0]['view'];?></td></tr>
<tr><th>Web site</th><td><?php echo $node->field_website[0]['view'];?></td></tr>
那么,现在基本上就快完工了;现在就剩下"description"(或者body)字段需要考虑了。这个与前面所讲的不一样,所以你必须要小心一点。这个字段是这样引用的,"$node->content['body']['#value']",我看到一些人在帖子中建议,"$node->description"也应该可以工作,但是现在还不能。
那么,让我们将整个例子正合到一起,希望我的讲解还算清晰明了:
<div class="content agency">
<?php echo theme('imagecache', 'thumbnail', $node->field_logo[0]['filepath'], $node->title, $node->title, array('align' => 'left', 'hspace' => '10')); ?>
<h2><big><?php echo l($node->title, 'node/'. $node->nid, array('title' => t('View agency')));?></big></h2><br/>
<table border="2" cellpadding="5">
<tr><th>Address</th><td>
<?php
echo $node->field_address[0]['street1'];
if ($node->field_address[0]['apt']) { echo '; '. $node->field_address[0]['apt']; }
if ($node->field_address[0]['street2']) { echo '<br/>'. $node->field_address[0]['street2']; }
echo '<br/>'. $node->field_address[0]['city'] .', '. $node->field_address[0]['state'] .' '. $node->field_address[0]['zip'];
if ($node->field_address[0]['country'] != 'US') { echo '<br/><big>'. $node->field_address[0]['country'] .'</big>'; }
?>
</td></tr>
<tr><th>Phone</th><td><?php echo $node->field_phone[0]['view'];?></td></tr>
<tr><th>Email</th><td><?php echo $node->field_email[0]['view'];?></td></tr>
<tr><th>Web site</th><td><?php echo $node->field_website[0]['view'];?></td></tr>
</table>
<?php
if ($node->content['body']['#value']) {
echo '<p><big>'. $node->content['body']['#value'] .'</big></p>';
}
?>
</div>
<div class="clear-block clear"></div>
While I initially developed this on the Bluemarine theme, it worked without change on the
这个模板文件,我最初是在Bluemarine主题下面开发的,当我把它放在Garland主题下时,只需要在我的CSS中加入".agency table {width: auto;}"就可以了。其它都不用修改。这是由于Garland主题忽略了我的"width"属性,并将其强制为100%的缘故。
最后…
现在将你的工作保存,并检查它是否正常工作。
将文件保存到你主题的文件夹下,取名为"node-content_type.tpl.php",其中content_type是你在第一步中给节点类型起的内部名字。
展示一列节点
站点已经安装了Views模块,这样我就可以使用这个模块来选择资源了,尽管我觉得这样运行效率低一些。
由于所有字段在主题中都列出来了,那么我在Views中需要做些什么呢?对于"teaser view",我发现所有要做的就是选择节点中的字段,比如标题等等,然后你就得到了想要的节点。你可能还想排个序,但是这已经不是本文讨论的范围了。
模板文件node.tpl.php适用于所有类型的节点的主题化.这个文件覆盖了所有的节点类型,你可以按照手册的node.tpl.php页面的说明,对这个文件进行修改.为了对单独的节点类型分别进行主题化,你需要在你的主题文件夹下面创建一个node-$type.tpl.php文件,其中$type是节点类型的名称,从而为每种类型按照需要自定义主题.一些例子:
在一般情况下,你可以将$type替换为任何节点类型的名称.一种特殊情况是, flexinode模块,它在内部使用数字对类型进行命名. Flexinodes的主题化可参看http://drupal.org/node/31646.还有,你也不能使用 node-admin.tpl.php仅仅对管理页面进行主题化.(译者注:实际上,是可以这样用的,但是你需要加点东西,在template.php中加个函数,但是这种用法不常见,文中所讲的应该是,不能直接这样用.)
创建了这个文件以后,将node.tpl.php的内容拷贝进取,然后按照要求进行修改. node.tpl.php列出的所有变量都可以使用.当Drupal展示该类型的节点时,就会使用你新创建的模板文件来代替默认的node.tpl.php.
对于由flexinode创建的节点类型,你给节点类型的命名(也就是展示的节点类型名)与系统内部使用的节点类型名不一样.在系统内部使用的节点类型名为"flexinode-1", "flexinode-2",等等.查看节点类型名的一种方式是,直接查看数据库表“node”的name列。
例如,如果你使用flexinode创建了一个名为"info"的节点类型,如果你查看数据库的话,你就会发现其内部名称为"flexinode-1"。所以,你为"info"创建的模板文件应该命名为"node-flexinode-1.tpl.php"。
你也可以通过访问admin>>content>>content types页面,点击所选的节点类型,来查看flexinode后面的数字。这个数字将出现在url(比如,node/types/edit_type/1就意味着flexinode-1)。
在你的节点模板中(比如node-flexinode-1.tpl.php),你可以这样引用flexinode的不同字段:
<?php print $node->flexinode_1 ?>
导航到admin>>content>>content types页面,点击编辑所选字段链接,查看相应的url(比如node/type/edit_field/1,就意味着$node->flexinode_1),你会发现不同的字段的引用方式是类似的。
这种方式适用于文本输入框,文本字段,日期/时间。对于图片你可以这样:
<img src="/<?php print $node->flexinode_1->filepath ?>" />.
如果需要输出对象的话,你需要研究一下代码了。你可以使用下面的输出命令:
<?php print serialize($node->flexinode_1)?>
它看起来有点怪,但它是一序列的变量和它们的内容。你可以找出满足你需要的那个。如果你找出了特定字段的工作方式的话,你可以将代码贴到这里来。
我看到很多人发帖询问如何为OG首页定制主题.我自己也想对drupal4.7版本的旧式OG首页进行升级,但是我还不能找到一种像样的方式。文档中(至少我看到的是)看起来仅仅告诉我们如何为实际的group(组)节点类型定制主题(例如node-og.tpl.php),或者在默认的og_ghp_ron主题加点过滤器。这些都不是本文要讨论的。(译者注,这里所讲的首页,是小组的首页,而不是站点的首页,一个站点可以有n多个小组,每个小组有一个自己的首页面)。
让我们假定你想为你的OG首页展示的所有帖子定制主题的话。你想让这些在OG展示的帖子与它们独立的节点页面以及teaser列表页面有所区别。而对于所有的小组首页采用相同的主题方式。这是我所要讲解的(如果你想让每个小组的首页都不同的话,你仍然可以使用这个代码,或者你还可以使用views)。我觉得应该有更好的方式,我也听到有人说,这很容易,但是没有看到过讲解如何去实现的帖子。所以我只能采用我的笨方法了。
在本教程中,我们将定你的og节点类型叫做'og'。如果你不知道你的叫做什么,可以访问admin/og/og,并点击"group home page"。这里所选的就是你的og节点类型。
还有,你需要先备份你的站点,这样当你觉得不对劲的时候,还可以恢复原貌。
我想使用一种通用的方式,因为我不想,当我添加一个新的小组时,需要更新我的节点文件。我也不想让小组的创建者为小组创建一个路径别名。(当然,如果只有一个人能够创建小组,而且小组的数量也不多的话,你也可以手工的在小组编辑表单页面设置小组的路径别名,而不用安装pathauto模块;这取决于你)。
我首先要做的是安装Pathauto模块。Pathauto允许你为你的任何内容创建用户友好的url路径。按照Pathauto模块的readme里面的说明,进行安装,完成以后,访问admin/settings/pathauto,点击"node path settings"。如果你仅仅想为你的小组添加别名的话,那么在节点部分的顶部,删除"Default path pattern (applies to all node types with blank patterns below)"(默认路径模式(适用于所有的节点类型,如果下面的模式为空的话))下面的东西。
如果你没有设置这一项的话,所有的内容都将采用默认路径模式来设置别名。Pathauto是一个非常方便的模块,如果你想采用默认的模式的话,我鼓励你好好的读一下配置页面的说明文字。对于本教程,就放心把它删除了吧。
现在,在同一部分里面,找到"Pattern for all og paths"(根据你的og节点类型名会有所不同)。在下面的输入框中,输入:groups/[title-raw]。
把页面往下拉,选中复选框"Bulk generate aliases for nodes that are not aliased"(为已有的没有别名的节点批量生成别名)。保存。现在你的所有小组首页面应该都有一个自动的用户友好的别名了。好极了。
现在,我们已经找到了一种方式,帮助节点识别我们是否处于小组首页面,现在就可以编辑我们的模板文件了。进入你的主题文件夹下面,编辑名为node.tpl.php的文件。
与普通的node.tpl.php文件唯一不同的是,我们需要检查pathauto别名,来看看我们是不是处在小组首页面上。然而,我们仅需要URL的第一个参数(也就是"groups"部分),所以我们还需要使用PHP字符串函数对别名进行截取。下面的是我写的代码片断,最后为完整的代码。下面是如何决定我们是否处于小组首页面:
<?php
if (arg(0) == 'node' && is_numeric(arg(1))) {
$url_alias = drupal_get_path_alias('node/'.arg(1));
} elseif (arg(0) == 'taxonomy' && arg(1) == 'term' && is_numeric(arg(2))) {
$url_alias = drupal_get_path_alias('taxonomy/term/'.arg(2));
}
// We need to only take the first argument of the aliased path, so define the separator
$slash = '/';
// find out if the separator exists in the returned alias
$pos = strpos($url_alias, $slash);
// if the separator is not present, then just look at the URL alias; if it is present, strip the first argument only
if ($pos === false) {
$string = $url_alias;
} else {
$string = substr($url_alias, 0, $pos);
}
// if the first argument of the aliased path == 'groups', then we know it's a group home page
if ($string == 'groups'){ ?>
接下来,我们将格式化小组首页面的节点。在这里,我仅需要一个带有链接的标题,以及作者,发表日期,所以代码如下:
<?php if ($page == 0) { ?>
<div class="content"><a href="<?php print $node_url; ?>"><?php print $title?></a><span class="submitted"><?php print " ".$user_name. " " . $date; ?></span></div>
<?php }; ?>
接下来,如果别名不是'groups'的话,我将提供一个普通的teaser视图(view):
<?php
// if the first argument of the aliased path wasn't 'groups', then it's a normal teaser view
}else{ ?>
<div class="node<?php if ($sticky) { print " sticky"; } ?><?php if (!$status) { print " node-unpublished"; } ?>">
<?php if ($picture) { print $picture; }?>
<?php if ($page == 0) { ?>
<h2 class="title"><a href="<?php print $node_url; ?>"><?php print $title?></a></h2>
<?php }; ?>
<span class="submitted"><?php print $submitted?></span> <span class="taxonomy"><?php print $terms?></span>
<div class="content"><?php print $content?></div>
<div class="clr">
<?php if ($links): ?>
<div class="links"><?php print $links; ?></div>
<?php endif; ?>
</div>
</div>
<?php } ?>
接着,让我们添加独立的节点视图(view),从而完成我们的node.tpl.php文件。
下面是完整的可用的代码
(你可以根据需要自己定制外观):
<?php if ($teaser): ?>
<?php
// we just get the drupal path args so that we can look up the path alias
if (arg(0) == 'node' && is_numeric(arg(1))) {
$url_alias = drupal_get_path_alias('node/'.arg(1));
} elseif (arg(0) == 'taxonomy' && arg(1) == 'term' && is_numeric(arg(2))) {
$url_alias = drupal_get_path_alias('taxonomy/term/'.arg(2));
}
// We need to only take the first argument of the aliased path, so define the separator
$slash = '/';
// find out if the separator exists in the returned alias
$pos = strpos($url_alias, $slash);
// if the separator is not present, then just look at the URL alias; if it is present, strip the first argument only
if ($pos === false) {
$string = $url_alias;
} else {
$string = substr($url_alias, 0, $pos);
}
// if the first argument of the aliased path == 'groups', then we know it's a group home page
if ($string == 'groups'){
// below this is where you need to put your node formatting for the og home page ?>
<?php if ($page == 0) { ?>
<div class="content"><a href="<?php print $node_url; ?>"><?php print $title?></a><span class="submitted"><?php print " ".$user_name. " " . $date; ?></span></div>
<?php }; ?>
<?php
// if the first argument of the aliased path wasn't 'groups', then it's a normal teaser view
}else{
// below this is where you need to put your normal node teaser formatting ?>
<div class="node<?php if ($sticky) { print " sticky"; } ?><?php if (!$status) { print " node-unpublished"; } ?>">
<?php if ($picture) { print $picture; }?>
<?php if ($page == 0) { ?>
<h2 class="title"><a href="<?php print $node_url; ?>"><?php print $title?></a></h2>
<?php }; ?>
<span class="submitted"><?php print $submitted?></span> <span class="taxonomy"><?php print $terms?></span>
<div class="content"><?php print $content?></div>
<div class="clr">
<?php if ($links): ?>
<div class="links"><?php print $links; ?></div>
<?php endif; ?>
</div>
</div>
<?php } ?>
<?php else:
// here is where your full node formatting goes ?>
<div id="node-<?php print $node->nid; ?>" class="node<?php if ($sticky) { print ' sticky'; } ?><?php if (!$status) { print ' node-unpublished'; } ?> clear-block">
<?php print $picture ?>
<?php if ($page == 0): ?>
<h2><a href="<?php print $node_url ?>" title="<?php print $title ?>"><?php print $title ?></a></h2>
<?php endif; ?>
<div class="meta">
<?php if ($submitted): ?>
<span class="submitted"><?php print $submitted ?></span>
<?php endif; ?>
<?php if ($terms): ?>
<span class="terms"><?php print $terms ?></span>
<?php endif;?>
</div>
<div class="content">
<?php print $content ?>
</div>
<?php
if ($links) {
print $links;
}
?>
</div>
<?php endif; ?>
Hopefully that will help save somebody time.
希望本文对读者能有所帮助。
你可以在drupal主题的node.tpl.php模板文件中使用下面的条件语句,来为摘要创建一个独特的外观:
<?php
if ($teaser) {
//if node is being displayed as a teaser
//Anything here will show up when the teaser of the post is viewed in your taxonomies or front page
} else {
//all other cases
//Anything here will show up when viewing your post at any other time, e.g. previews
?>
或者,你想在条件语句中使用HTML,例如使用模板创建两个完全不同的布局:
<?php if ($teaser): ?>
<!-- teaser template HTML here -->
<?php else: ?>
<!-- regular node view template HTML here -->
<?php endif; ?>
注意$page变量用来判断节点是不是单独处在一个页面中,这与是否是作为摘要(teaser)显示的是有区别的:在节点的编辑预览页面, $page都为true的.
这个模板文件定义了页面的骨架.
可用变量(按字母顺序)
$base_path
返回Drupal安装的URL基路径.最初,这将默认为/。
$breadcrumb
在页面顶部用于展示面包屑的breadcrumb。
$closure
需要放在页面的底部,在页面展示出来后,用于动态javascript的调用。
$content
由Drupal生成的要展示的HTML内容。
$css
一个数组,包含了当前页面的所有CSS文件。
$directory
主题所在的目录,比如themes/garland or themes/garland/minelli.
$feed_icons
一个字符串,表示当前页面的所有种子的图标。
$footer_message
在管理设置页面(admin/settings/site-information)定义的页脚信息。
$head
由drupal_get_html_head()生成的HTML。
$head_title
在页面标题中要展示的文本。
$help
动态帮助文本,主要用于drupal的后台管理页面。
$is_front
如果当前页面为首页的话返回true。一般和mission(使命)结合使用。
$language
Drupal站点当前使用的语言。
$layout
这个设置允许你为不同的布局('none', 'left', 'right' 或者'both')使用不同的样式,这以来于你的布局结构。
$logo
Logo图片的路径,可在主题配置页面中定义。
$messages
HTML状态信息和错误信息,展示在页面的顶部。
$mission
Drupal站点的使命文本,当在主题设置中禁用了的话,那么为空。
$node
(drupal5.x及更高版本)如果你要在page.tpl.php中展示一个单独的完整的节点页面视图的话,那么就可以在你的模板中使用$node变量.
$onload_attribute
(drupal4.7及以前版本)在<head>标签中,添加onload标签,从而允许附带脚本的自动执行。
$primary_links (array)
一个包含了链接的数组,可以在phptemplate特定的配置区块中定义这些链接。
$scripts
(drupal5.x及更高版本)用来加载JavaScript文件,并使得JS设置可用的HTML。在以前,javascript文件是硬编码到page.tpl.php中去的。
$search_box
如果启用了搜索框的话,返回true。
$search_button_text
(drupal4.7及以前版本)搜索按钮上的翻译了的文本。
$search_description
(drupal4.7及以前版本)搜索按钮的翻译了的描述。
$search_url
(drupal4.7及以前版本)搜索表单要提交到的URL。
$secondary_links (array)
一个包含了链接的数组,可以在phptemplate特定的配置区块中定义这些链接。
$sidebar_left
用于左栏的HTML内容。
$sidebar_right
用于右栏的HTML内容。
$site_name
Drupal站点的名字,当在主题设置中禁用了该项的话,那么为空。
$site_slogan
Drupal站点的口号,当在主题设置中禁用了该项的话,那么为空。
$styles
用于样式表的切换。这将输出所需要的样式标签。
$tabs
在页面顶部展示的HTML标签(tab)
$title
标题,与head_title是不同的,这个在大多数时候指的是节点标题。
默认模板
下面是Drupal 5的bluemarine主题的page.tpl.php模板,让你对页面模板文件有个实际的认识。
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="<?php print $language ?>" xml:lang="<?php print $language ?>">
<head>
<title><?php print $head_title ?></title>
<?php print $head ?>
<?php print $styles ?>
<?php print $scripts ?>
<script type="text/javascript"><?php /* Needed to avoid Flash of Unstyle Content in IE */ ?> </script>
</head>
<body>
<table border="0" cellpadding="0" cellspacing="0" id="header">
<tr>
<td id="logo">
<?php if ($logo) { ?><a href="<?php print $base_path ?>" title="<?php print t('Home') ?>"><img src="<?php print $logo ?>" alt="<?php print t('Home') ?>" /></a><?php } ?>
<?php if ($site_name) { ?><h1 class='site-name'><a href="<?php print $base_path ?>" title="<?php print t('Home') ?>"><?php print $site_name ?></a></h1><?php } ?>
<?php if ($site_slogan) { ?><div class='site-slogan'><?php print $site_slogan ?></div><?php } ?>
</td>
<td id="menu">
<?php if (isset($secondary_links)) { ?><?php print theme('links', $secondary_links, array('class' =>'links', 'id' => 'subnavlist')) ?><?php } ?>
<?php if (isset($primary_links)) { ?><?php print theme('links', $primary_links, array('class' =>'links', 'id' => 'navlist')) ?><?php } ?>
<?php print $search_box ?>
</td>
</tr>
<tr>
<td colspan="2"><div><?php print $header ?></div></td>
</tr>
</table>
<table border="0" cellpadding="0" cellspacing="0" id="content">
<tr>
<?php if ($sidebar_left) { ?><td id="sidebar-left">
<?php print $sidebar_left ?>
</td><?php } ?>
<td valign="top">
<?php if ($mission) { ?><div id="mission"><?php print $mission ?></div><?php } ?>
<div id="main">
<?php print $breadcrumb ?>
<h1 class="title"><?php print $title ?></h1>
<div class="tabs"><?php print $tabs ?></div>
<?php print $help ?>
<?php print $messages ?>
<?php print $content; ?>
<?php print $feed_icons; ?>
</div>
</td>
<?php if ($sidebar_right) { ?><td id="sidebar-right">
<?php print $sidebar_right ?>
</td><?php } ?>
</tr>
</table>
<div id="footer">
<?php print $footer_message ?>
</div>
<?php print $closure ?>
</body>
</html>
PHPTemplate在page.tpl.php中提供了一个变量,如果你知道怎么使用的话,它将非常强大.
Drupal 5 ($layout)
$layout包含一个字符串,用来告诉你页面的布局:
如果用的是左栏的话,则为left
如果用的是右栏的话,则为right
如果左右栏都用的话,则为both
Drupal 6 ($body_classes)
一个更强大的变量, $body_classes变量包含了一些由空格分隔的非常有用的信息,你可以在你的css类中使用它.
一些Drupal 5主题已经实现了这些类,比如Zen。
用它来增加你的优势
当你把这些变量放到类中的时候,这将使得你的主题更强大,扩展性也更强。这些变量的字符串,就是专门针对CSS类进行设计的。
使用drupal_add_css()函数可以将其它的样式表添加到样式表数组中去:
<?php
// Repeat this line for every CSS file added.
drupal_add_css(path_to_theme() . '/example.css', 'theme', 'all', TRUE);
// Reassemble the $styles snippet to include the new files.
$styles = drupal_get_css();
?>
在这个例子中,我们是从主题文件夹下取得的example.css文件.当然你也可以把样式表放到files目录下,这样就不用使用path_to_theme()函数了,此时你就可以直接这样用'files/subdirectory/example.css'.
drupal_add_css()函数的参数分析:
对于Drupal 4.7.x
<?php
print $head;
print theme('stylesheet_import', base_path() . path_to_theme() . '/your_extra.css');
print $styles;
?>
因此,这就是样式表的最终顺序:
样式表可以对前面的CSS规则进行覆写(例如,style.css可以覆写它前面的所有的CSS,除了用户CSS)。
在Drupal 5.0中, PHPTemplate支持在一个主题下,使用多个页面模板.根据当前的url路径(例如node/1, taxonomy/term/2, 或者user/1),PHPTemplate会按照顺序寻找相应的模板,如果找不到的话,将会采用默认的page.tpl.php模板文件。
例如,如果你访问http://www.example.com/node/1/edit,PHPtemplate将按照降序寻找下面的模板:
page-node-edit.tpl.php
page-node-1.tpl.php
page-node.tpl.php
page.tpl.php
如果你访问的是http://www.example.com/tracker,PHPTemplate将寻找下面的模板:
page-tracker.tpl.php
page.tpl.php
这一规则还适用于用户和分类的url。当访问http://www.example.com/user/1时 ,PHPtemplate将按照降序寻找下面的模板:
page-user-1.tpl.php
page-user.tpl.php
page.tpl.php
注意,首页是个特殊的情况(它的URL是http://example.com,后面没有相对路径了)。对于这种情况,你可以使用下面的模板:
page-front.tpl.php
记住,这些模板建议是基于drupal的内部路径的。如果你使用了path 或者pathauto模块的话,那么你看到的将是路径的别名。但是对于这些模板来说,他们仍然是基于drupal内部路径的。如果你想基于URL别名使用模板的话,可参看基于URL别名的不同页面模板一文。
如果你需要基于其它的一些规则来切换页面模板文件的话(例如,登录用户的角色),你可以在你主题的template.php文件中实现phptemplate_variables()函数。$vars['template_files']变量存储了一个可能的tpl.php文件数组,使用先进后出的规则。
注意:对于Drupal 5,你可参看相应的文章http://drupal.org/node/104316。
你可以为单篇文章、URL路径、分类术语、章节、和你的首页定制主题。
对于节点、页面和其它的主题定制的更全面的例子,可参看手册的PHP Template Snippets >> Customising full page layouts and sections。
一种方式是使用sections module,来为你站点的不同部分使用不同的主题。然而,如果你的自定义主题或者模块拥有自己的主题模板文件的话,比如event(事件)模块,它需要许多重复的CSS和图片资源。
在一个Drupal站点上使用多个主题的关键是,由主题引擎决定当前你在站点中所处的位置。下面是我们可以从模板引擎中检查的一列条件。
$is_front phptemplate变量可用来检查当前是否处于首页。
<?php
if ($is_front) {
include('front.tpl.php');
return;
}
?>
Drupal的arg()函数用来得到路径中的参数。
arg(0)=="admin"// is /admin
arg(0) =="node"// is /node
arg(0)=="user" // is /user
arg(0)=="node"&&arg(1)=="add" // is /node/add
arg(0)=="node"&& arg(2)=="edit" // is /node/###/edit
arg(0)=="user"&&arg(1)=="add" // is /user/add
arg(0)=="admin"&&arg(1)="user"&&arg(2)=="create" // is /admin/user/create
arg(0)=="alias"&&arg(1)=="alias1" is /alias/alias1
arg(0)=="taxonomy"&&arg(1)=="term"&&arg(2)=="term#" // is /taxonomy/term/term#
//arg(1)=="comment"
//arg(2)=="reply"
一旦你知道了front_page的值,或者路径,那么我们就可以使用不同的主题模板,也可使用CSS文件,或者修改xHTML标签的CSS类了。本节将分成3个子页面。
<?php
if ($is_front) {
include 'front.tpl.php';
return;
}
elseif (arg(0)=="taxonomy"&&arg(1)=="term"&&arg(2)=="term#") { // add argument of choice
include 'term#.tpl.php';// add template of choice
return;
}
<?php
if ($is_front) {
include 'page_front.tpl.php';
return;
}
if ($node->type == 'nodetype') { // all pages on my site are 'nodetype'
$my_path = explode("/", drupal_get_path_alias('node/'.$node->nid)); // all pages have path like 'section/page'
$my_path = $my_path[0];
} else {
$my_path = arg(0);
}
switch ($my_path) {
case 'using':
$section = 1;
break;
case 'education':
$section = 2;
break;
case 'company':
$section = 3;
break;
case 'image':
$section = 4;
break;
case 'forum':
$section = 5;
break;
default:
$section = 0;
}
$css_section = 'section'.$section;
?>
PHPTemplate 主题:
在page.tpl.php的body标签中(来自于bluemarine):
<body <?php print $onload_attributes ?> >
将变为:
<body class=" <?php print arg(0); ?> " <?php print $onload_attributes ?> >
接着你就可以为管理页面使用.admin作为css父选择器了。
对于Smarty
在page.tpl的body标签中(来自于bluemarine_smarty):
<body{$onload_attributes}>
将变为:
<body class="{php} echo arg(0); {/php}"{$onload_attributes}>
当查看根节点时,这可能产生一个空的class属性值---可以在class中包含一个固定的前缀,从而阻止这种情况的发生。
下面的代码片断,用来检查当前节点是否匹配一个主菜单链接.这段代码假定主菜单链接的格式为'/somepage'.
<?php if (count($primary_links)) : ?>
<ul class="primary">
<?php foreach ($primary_links as $link): ?>
<?php preg_match("/<a\s*.*?href\s*=\s*['\"]([^\"'>]*).*?>(.*?)<\/a>/i", $link, $matches); ?>
<?php if (('/'.drupal_get_path_alias('node/'.$node->nid))==$matches[1]): /* if $node exists */ ?>
<li class="selected"><?php print $link?></li>
<?php elseif ('/'.arg(0)==$matches[1]): /* else try to use arg */ ?>
<li class="selected"><?php print $link?></li>
<?php elseif ((drupal_get_path_alias('node/'.$node->nid)=='node/') && (arg(0)=='node') && ($matches[1]=='/')): /* else if home */ ?>
<li class="selected"><?php print $link?></li>
<?php else: ?>
<li><?php print $link?></li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
<?php endif; ?>
在这段代码中,我们只使用了arg(0)进行检查,对于那些由drupal模块创建的类节点来说,可能并不适用.
这个代码将为你drupal站点上的每个页面的<body>标签生成一个类(class)和id。
在你主题的页面模板文件(page.tpl.php)中,将已存在的<body>标签替换为下面的代码:
<body<?php print phptemplate_body_attributes($is_front, $layout); ?>>
并在你主题的template.php文件中添加下面的代码:
/**
* Sets the body tag class and id attributes.
*
* From the Theme Developer's Guide, http://drupal.org/node/32077
*
* @param $is_front
* boolean Whether or not the current page is the front page.
* @param $layout
* string Which sidebars are being displayed.
* @return
* string The rendered id and class attributes.
*/
function phptemplate_body_attributes($is_front = false, $layout = 'none') {
if ($is_front) {
$body_id = $body_class = 'homepage';
}
else {
// Remove base path and any query string.
global $base_path;
list(,$path) = explode($base_path, $_SERVER['REQUEST_URI'], 2);
list($path,) = explode('?', $path, 2);
$path = rtrim($path, '/');
// Construct the id name from the path, replacing slashes with dashes.
$body_id = str_replace('/', '-', $path);
// Construct the class name from the first part of the path only.
list($body_class,) = explode('/', $path, 2);
}
$body_id = 'page-'. $body_id;
$body_class = 'section-'. $body_class;
// Use the same sidebar classes as
$sidebar_class = ($layout == 'both') ? 'sidebars' : "sidebar-$layout";
return " id=\"$body_id\" class=\"$body_class $sidebar_class\"";
}
注意:这里你需要启用简洁url,才能正常使用。
使用例子:
使用了这段代码后,当你访问news/archive/2007页面时,你将看到下面的<body>属性:
<body id="page-news-archive-2007" class="section-news sidebar-left">
现在你要做的就是为整个站点的新闻栏目创建一套新的CSS规则:
.content { background-image: url("background.png"); }
.section-news .content { background-image: url("news.png"); }
如果你启用了路径别名模块的话,并且花了功夫为你的页面创建了多层级的路径,这样你就很容易将站点分为做个栏目,然后为每个栏目使用一个完全不同的布局(背景图片,颜色,宽度等等)。
Drupal 5包含了一个非常有用的机制,可以为不同的页面、区块、节点等提供不同的模板。例如,你可以为'blog'节点类型添加一个模板node-blog.tpl.php,从而取代默认的node.tpl.php模板。对于页面模板,就更加方便了,所以你可以为特定节点创建特定的模板,可参看基于当前路径适用不同的页面模板一文。
通过_phptemplate_variables()函数,还可以编辑可能的"template suggestions"(模板建议)列表。例如,下面的代码片断就基于URL别名创建了额外的“suggestions”(建议)。这比使用内部Drupal路径的方法更强大。
<?php
function _phptemplate_variables($hook, $vars = array()) {
switch ($hook) {
case 'page':
// Add node template suggestions based on the aliased path.
// For instance, if the current page has an alias of about/history/early,
// we'll have templates of:
// node_about_history_early.tpl.php
// node_about_history.tpl.php
// node_about.tpl.php
// Whichever is found first is the one that will be used.
if (module_exists('path')) {
$alias = drupal_get_path_alias($_GET['q']);
if ($alias != $_GET['q']) {
$suggestions = array();
$template_filename = 'node';
foreach (explode('/', $alias) as $path_part) {
$template_filename = $template_filename . '_' . $path_part;
$suggestions[] = $template_filename;
}
}
$vars['template_files'] = $suggestions;
}
break;
}
return $vars;
}
?>
可以在你的template.php文件中修改一个模板的“模板建议”("template suggestions")列表。下面的代码片断,将根据当前页面的节点类型来添加页面模板建议。这样,你就可以为节点类型为新闻(news)的页面定义一个模板page-nodetype-news.tpl.php。
<?php
// Add additional template suggestions
function _phptemplate_variables($hook, $vars) {
switch ($hook) {
case 'page':
// Add page template suggestions based on node type, if we aren't editing the node.
if ($vars['node'] && arg(2) != 'edit') {
$vars['template_files'][] = 'page-nodetype-'. $vars['node']->type;
}
break;
}
return $vars;
}
?>
如果你使用的drupal主题为Zen的话,打开你的子主题的template.php文件,将SUBTHEME_preprocess_page()函数的注释去掉,并向其中添加以下代码(没有<?php 和?>标签):
<?php
// Add page template suggestions based on node type, if we aren't editing the node.
if ($vars['node'] && arg(2) != 'edit') {
$vars['template_files'][] = 'page-nodetype-'. $vars['node']->type;
}
?>
可以在你的template.php文件中修改一个模板的“模板建议”("template suggestions")列表。下面的代码片断,将根据当前页面的URL别名来添加页面模板建议。这样你就可以为'music'路径或者目录下面的页面定义一个page-music.tpl.php模板文件了。
Drupal 5版本:
<?php
function _phptemplate_variables($hook, $vars = array()) {
switch ($hook) {
case 'page':
// Add page template suggestions based on the aliased path.
// For instance, if the current page has an alias of about/history/early,
// we'll have templates of:
// page-about-history-early.tpl.php
// page-about-history.tpl.php
// page-about.tpl.php
// Whichever is found first is the one that will be used.
if (module_exists('path')) {
$alias = drupal_get_path_alias(str_replace('/edit','',$_GET['q']));
if ($alias != $_GET['q']) {
$suggestions = array();
$template_filename = 'page';
foreach (explode('/', $alias) as $path_part) {
$template_filename = $template_filename . '-' . $path_part;
$suggestions[] = $template_filename;
}
}
$vars['template_files'] = $suggestions;
}
break;
}
return $vars;
}
?>
Drupal 6版本:
<?php
function phptemplate_preprocess_page(&$vars) {
if (module_exists('path')) {
$alias = drupal_get_path_alias(str_replace('/edit','',$_GET['q']));
if ($alias != $_GET['q']) {
$suggestions = array();
$template_filename = 'page';
foreach (explode('/', $alias) as $path_part) {
$template_filename = $template_filename . '-' . $path_part;
$suggestions[] = $template_filename;
}
}
$vars['template_files'] = $suggestions;
}
}
?>
变通
下面的是这个方法的一些变通应用。
在一个页面模板中仅修改一个CSS类。适用于那些不需要创建一个新的模板,仅需要修改CSS的情况。
<?php
if (module_exists('path')) {
$alias = drupal_get_path_alias($_GET['q']);
$class = explode('/', $alias);
print '<div class="page page-'.$class[0].'">';
}
?>
对特定节点类型的修改限制:
<?php
// (...)
if(arg(0)=='node') {
$vars['template_files'] = 'page_' . $vars['node']->type;
}
// (...)
?>
这里的例子来自于Drupal的论坛讨论. $hook指的是变量可用的区域(比如,对于comment.tpl.php,它就是"comment").
这个函数需要定义在一个template.php文件中,这个文件放在主题目录下面(例如: themes/box_cleanslate/template.php).
<?php
function _phptemplate_variables($hook, $vars) {
switch($hook) {
case 'comment' :
$vars['newvar'] = 'new variable';
$vars['title'] = 'new title';
break;
}
return $vars;
}
?>
这个函数对phptemplate_comment返回的变量进行了处理,你在这里可以很方便的按照需求对变量进行调整.
现在,在你的comment.tpl.php文件中,可以使用一个新的名为$newvar的变量了.类似的, $title变量覆写了phptemplate_comment中该变量的值.
一个漂亮的技巧是用来计算每个钩子的调用次数,这样你可以传递过来另一个额外的参数了.如下所示:
<?php
function _phptemplate_variables($hook, $vars) {
static $count;
$count = is_array($count) ? $count : array();
$count[$hook] = is_int($count[$hook]) ? $count[$hook] : 1;
$vars['zebra'] = ($count[$hook] % 2) ?'odd' : 'even';
$vars['seqid'] = $count[$hook]++;
return $vars;
}
?>
如果为偶数的话,就是'even';奇数的话就是'odd'.这意味着你可以为每个节点/区块/评论/其它,实现一个斑马线样式(也就是:颜色间隔切换).
接着你就可以为class='$zebra'设置一些样式了,用来处理颜色的切换.
另一个例子就是,节点标记,用来判断我们查看的是不是节点.当有人查看一片文章时,使用这个变量就可以处理不同项目的显示了.
<?php
function _phptemplate_variables($hook, $vars) {
switch ($hook) {
case 'page':
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == '') {
$vars['content_is_node'] = TRUE;
}
break;
}
return $vars;
}
?>
注意,这里的切换有点老套,但是我还是这样用了,因为将来你可能需要添加更多的变量.在这种情况下,你就需要它了.
args()用来检查你的url是/node/NID/形式的,而不是/node/NID/edit或者/node形式的.如果找到了符合条件的,我们将这个标记设为TRUE.
从Drupal 4.7开始,主题可以实现任意数量的区域,用于区块或者其它内容的显示。你可以到Drupal的区块管理页面(admin/build/block)看一下,你将看到,实际上区域本质上就是一个容器,你可以向里面添加区块。你也可以使用PHP向区域中添加非区块内容。什么是区域呢,一个比较好的例子就是边栏。
这些区域可以用于整个页面,或者其它的主题元素中,比如接电或者评论。
PHPTemplate引擎定义了5个默认的区域:left(左栏), right(右栏), content(内容), header(页首), 和footer(页脚)。为了实现这些区域,在drupal主题中,只需要在page.tpl.php模板文件中包含进变量$sidebar_left, $sidebar_right, $content, $header, 和$footer_message就可以了,比如,<?php print $header; ?>。
如何实现自定义区域
对于一个主题,如果你不想使用默认的区域,想自己定义一套的话,你可以使用_regions()钩子函数。在这种情况下,你的自定义区域,将会覆写PHPTemplate的默认区域。
如果在主题目录中还没有template.php文件的话,那么创建一个;如果有的话,就使用现有的。然后,打开template.php文件,向里面添加一个mytheme_regions()函数,用来定义你的区域。每个区域都需要一个名字(没有空格的字符串)和一个描述(展示给用户的文本,比如在区块管理页面)。在下面的代码中,我们为"mytheme"主题重新定义了除左栏以外的PHPTemplate的默认区域,还为它添加了两个新的区域。
<?php
function mytheme_regions() {
return array(
'right' => t('right sidebar'),
'content' => t('content'),
'header' => t('header'),
'footer' => t('footer'),
'floater' => t('floater'),
'inline1' => t('inline 1')
);
}
?>
注意:对于函数名,你需要注意几点:
如何向页面写入主题的区域变量。
如果你需要在page.tpl.php中使用你的区域的话,你不需要为你的区域去创建一个变量,然后再将内容指定给它;PHPTemplate已经自动帮你完成了。你所需要做的就是,通过编辑你主题的page.tpl.php文件,将变量写入到页面。对于你定义的每个新的区域,只需要在page.tpl.php中加一个print语句就可以了。对于前面定义的'floater'区域,可以这样调用:
<?php print $floater;?>.
当然,你可以使用HTML,CSS,PHP(这个可能用到)来为你的区域定制一个好看的外观。
默认情况下,所有定义的区域都传递给page.tpl.php。但是只需要额外的几步,你也可以将特定的区域应用到其它的模板文件中:node.tpl.php,comment.tpl.php等等。下面是如何实现的。
在你定义区域的template.php文件中,定义一个_phptemplate_variables()函数(如果已经存在的话,就使用已有的)。在这里,你要做的就是将区域内容指定到一个特定的主题调用中。当调用_phptemplate_variables()时,将会向$hook变量传递一个主题参数,比如'node'。所以,使用下面的代码,我们可以将区域指定到节点模板文件中:
<?php
function _phptemplate_variables($hook, $variables) {
// Load the node region only if we're not in a teaser view.
if ($hook == 'node' && !$vars['teaser']) {
// Load region content assigned via blocks.
foreach (array('inline1') as $region) {
$variables[$region] = theme('blocks', $region);
}
}
return $variables;
}
?>
注意,我们在这里做了检查,确保不是处于一个'teaser'视图中,这样只有当处于完整的节点视图中时,才加载内置区域。
现在,在你的节点模板中,你就可以把区域变量包含进来了。下面是标准的phptemplate node.tpl.php模板加进'inline1'区域后的样子:
<div class="node<?php if ($sticky) { print " sticky"; } ?><?php if (!$status) { print " node-unpublished"; } ?>">
<?php if ($picture) {
print $picture;
}?>
<?php if ($page == 0) { ?><h2 class="title"><a href="<?php print $node_url?>"><?php print $title?></a></h2><?php }; ?>
<span class="submitted"><?php print $submitted?></span>
<span class="taxonomy"><?php print $terms?></span>
<div class="content"><div class="floatleft"><?php print $inline1?></div><?php print $content?></div>
<?php if ($links) { ?><div class="links">» <?php print $links?></div><?php }; ?>
</div>
大多数情况下,你都会为你的区域添加一些样式,你可以像往常一样,通过主题的style.css文件来完成。例如,在这里,我们将让内置区域向左浮动:
div.floatleft {
float: left;
}
由于一个节点的变量传递给了其它的节点模板,所以你也可以在node-type模板中实现你的自定义外观。
这种方式还可以应用到其它的主题元素中。下面的例子将特定的区域指定到了节点和评论中:
<?php
function _phptemplate_variables($hook, $variables) {
// Load region content assigned via blocks.
// Load the node region only if we're not in a teaser view.
if ($hook == 'node' && !$variables['teaser']) {
foreach (array('node1', 'node2') as $region) {
$variables[$region] = theme('blocks', $region);
}
}
else if ($hook == 'commment') {
foreach (array('comment1', 'comment2') as $region) {
$variables[$region] = theme('blocks', $region);
}
}
return $variables;
}
?>
如果你想将内容指定到区域中,而又不通过区块来实现的话,你可以使用drupal_set_content()函数。这允许你绕过通常的区块机制,你可以尝试以下几点:
将内容设置到区域中
在你的模块代码中,将内容设置给区域。在这里,你可以使用themename_regions()数组中不存在的区域名。这样,这些区域对于区块来说就不可用,因此里面也就不会放置区块内容了。假定你的区域叫做'region1' 和'region2'。在你的模块中,可以这样做:
<?php
$output = 'whatever';
drupal_set_content('region1', $output);
?>
在template.php文件中,设置一个变量,并将区域内容指定给这个变量。
<?php
function _phptemplate_variables($hook, $variables) {
// Load region content assigned via drupal_set_content().
if ($hook == 'page') {
foreach (array('region1', 'region2') as $region) {
$variables[$region] = drupal_get_content($region);
}
}
return $variables;
}
?>
输出你的内容
在你的page.tpl.php文件中,在需要的地方输出区域:
<?php
print $region1;
?>
如果你要覆写的主题函数不包含在基本列表中(block, box, comment, node, page),你需要将它告诉给PHPTemplate.
为了实现这一点,你需要在你主题目录下,创建一个template.php文件.这个文件以PHP开始标签<?php,但是这里不需要结束标签,推荐你不要使用结束标签.还有,在文件中,还需要包含用于主题覆写的存根(stubs),这些存根告诉引擎使用哪个模板文件,以及向其中传递哪些参数.
首先,你需要知道你要覆写哪个主题函数.你可以在Drupal API文档中找出一列这样的函数.我们在这里以theme_item_list()为例.
theme_item_list()大概是这样定义的:
<?php function theme_item_list($items = array(), $title = NULL) { ?>
现在,你需要在你主题的template.php文件中加一个存根,如下所示:
<?php
/**
* Catch the theme_item_list function, and redirect through the template api
*/
function phptemplate_item_list($items = array(), $title = NULL) {
// Pass to phptemplate, including translating the parameters to an associative array.
// The element names are the names that the variables
// will be assigned within your template.
return _phptemplate_callback('item_list', array('items' => $items, 'title' => $title));
}
?>
我们将函数名中的“theme”替换为了“phptemplate”,并调用函数_phptemplate_callback()以将参数($items和$title)传递给PHPTemplate。
现在,你可以在你主题目录下,创建一个item_list.tpl.php模板文件了,它将用来替代原有的theme_item_list()。这个函数,与原有的theme_item_list()相比,应该采用相同的逻辑。
注意,你需要访问[ Administer -> Site building -> Themes ],从而刷新PHPTemplate的缓存,以识别出新添加的文件。但是从drupal4.6开始,就不需要这一步了。
除了Drupal标准的主题函数以外(API中所包含的),你还可以使用主题函数来覆写Drupal表单的外观。例如,对于由表单构建器函数构建的一个核心表单,你可以使用form_id来对其进行覆写。详情可参看这个实例。
Drupal 6.x提供了一种内置的phptemplate方式,对站点离线后的维护页面进行主题化---参看http://drupal.org/node/195435.
在Drupal 5.x中,许多帖子建议通过修改内核为该页面定制主题,但是这种想法很臭,而且没有必要,当然最后的结果也不一定好.下面是由Nax(http://drupal.org/user/25511)提出的一些比较好的方式,每种方式都有自己的优点:
function phptemplate_maintenance_page($content, $messages = TRUE, $partial = FALSE) {
drupal_goto('path/to/your/site-offline.html');
}
参看http://drupal.org/node/58562#comment-281490
这两种方法都能工作,所以你可以根据你的情况选择一个适合自己的。
当开发你自定义的维护页面时,你需要注意几点:
如果你有什么建议或者更好的方法的话,可以在下面跟贴。
Drupal内置的CSS聚合工作原理是,在"files/css"文件夹下,创建一个聚合文件,可以创建多个这样的聚合文件,比如当一个css文件在特定页面不需要时,而在另一个页面则用得到,这时就会新建一个聚合文件,通过$styles变量将其输出到page.tpl.php中。然而,这种方式对于部分人是行不通的,例如,将drupal运行在多台前台终端服务器上,但是却没有共享的"files"文件夹(它们可以与主服务器进行同步,而编辑则向主服务器添加内容),并且启用了缓存。在这种情况下,在部分前台终端服务器上css就显示不出来了,这是由于返回的是缓存页面,而没有检查css文件是否存在。
这个问题的解决方案是,由template.php来负责css的聚合,并将css文件缓存在主题目录下(在服务器间同步时,通常将这个目录排除在外,即使你包含了这个目录,它仍然管用)。
每当用户访问一个前台终端服务器时,将会对文件名和最近修改时间应用md5函数从而创建一个字符串。如果存在了一个以该字符串为名的文件的话,将会使用这个文件,否则,将会创建一个这样的文件,并将其保存到'cache'目录下。
所以,首先在你的drupal主题下面创建一个名为'cache'的子目录,修改相应的权限,这样当代码执行时可以向该子目录写入文件。
如果你使用了Zen主题,那可可以向你子主题的template.php添加下面的代码(首先需要检查一下,这个函数是否已经存在,如果存在的话,你需要把函数声明去掉,将代码添加到函数的最底部),将STARTERKIT改为你主题的名字。
function STARTERKIT_preprocess_page(&$vars) {
$css = drupal_add_css();
$css_arr = array();
$modifiedDates = '';
$fileString = '';
foreach($css['all']['module'] as $css_module => $css_module_on) {
if(file_exists($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module)) {
$modifiedDates .= filemtime($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module);
$css_arr[] = $_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module;
$fileString .= $css_module . ',';
}
}
foreach($css['all']['theme'] as $css_theme => $css_theme_on) {
if(file_exists($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme)) {
$modifiedDates .= filemtime($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme);
$css_arr[] = $_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme;
$fileString .= $css_theme . ',';
}
}
$combinedContent = '';
$fileName = base_path() . path_to_subtheme() . '/cache/' . md5($fileString . $modifiedDates) . '.css';
$file = $_SERVER['DOCUMENT_ROOT'] . $fileName;
if(!file_exists($file)) {
foreach($css_arr as $css_file) {
$combinedContent .= PHP_EOL.PHP_EOL.file_get_contents($css_file);
}
$fh = fopen($file, 'w');
fwrite($fh, $combinedContent);
fclose($fh);
}
$vars['styles_aggregated'] = '<style type="text/css" media="all">@import "' . $fileName . '";</style>';
}
如果你用的是一个普通的主题的话,你需要向你的template.php文件中添加以下代码(同样,存在函数已经存在的情况):
function _phptemplate_variables($hook, $vars) {
switch($hook) {
case 'page' :
$css = drupal_add_css();
$css_arr = array();
$modifiedDates = '';
$fileString = '';
foreach($css['all']['module'] as $css_module => $css_module_on) {
if(file_exists($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module)) {
$modifiedDates .= filemtime($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module);
$css_arr[] = $_SERVER['DOCUMENT_ROOT'] . base_path() . $css_module;
$fileString .= $css_module . ',';
}
}
foreach($css['all']['theme'] as $css_theme => $css_theme_on) {
if(file_exists($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme)) {
$modifiedDates .= filemtime($_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme);
$css_arr[] = $_SERVER['DOCUMENT_ROOT'] . base_path() . $css_theme;
$fileString .= $css_theme . ',';
}
}
$combinedContent = '';
$fileName = base_path() . path_to_theme() . '/cache/' . md5($fileString . $modifiedDates) . '.css';
$file = $_SERVER['DOCUMENT_ROOT'] . $fileName;
if(!file_exists($file)) {
foreach($css_arr as $css_file) {
$combinedContent .= PHP_EOL.PHP_EOL.file_get_contents($css_file);
}
$fh = fopen($file, 'w');
fwrite($fh, $combinedContent);
fclose($fh);
}
$vars['styles_aggregated'] = '<style type="text/css" media="all">@import "' . $fileName . '";</style>';
break;
}
return $vars;
}
最后,在你的page.tpl.php中,注意将$styles替换为$styles_aggregated
下面这个例子,讲述了在drupal中,如何使用主题函数来隐藏元素.这个例子向你展示了如何隐藏节点创建页面生成的日志消息.它将永远的隐藏日志消息盒子(box),所以,如果你将来需要用到这个盒子的话,就不能使用这段代码了.
<?php
/**
* Override node form
*/
function phptemplate_node_form($form) {
// Remove 'Log message' text area
$form['log']['#access'] = FALSE;
return drupal_render($form);
}
?>
可以通过覆写theme_stylesheet_import()函数来忽略drupal.css文件.只需要向主题的template.php文件中添加以下代码:
<?php
/*
Do not include drupal's default style sheet in this theme !
*/
function phptemplate_stylesheet_import($stylesheet, $media = 'all') {
if (strpos($stylesheet, 'misc/drupal.css') == 0) {
return theme_stylesheet_import($stylesheet, $media);
}
}
?>
使用一个自定义.css文件来代替drupal.css:
<?php
function phptemplate_stylesheet_import($stylesheet, $media = 'all') {
if (strpos($stylesheet, 'misc/drupal.css') != 0) {
$stylesheet = str_replace('misc/drupal.css', 'misc/mysite.css', $stylesheet);
}
if (strpos($stylesheet, 'misc/drupal.css') == 0) {
return theme_stylesheet_import($stylesheet, $media);
}
}
?>
将这段代码放到主题的template.php文件中,这将drupal.css文件切换为一个自定义的.css文件。有些drupal站点,你想让它看不出来后台是用drupal做的话,你就需要替换这个drupal.css文件。在这个例子中,我们拷贝了原来的drupal.css文件,将其重命名为mysite.css,并将其保存到名为misc的目录下。
注意strpos的使用,来自于PHP.net:
警告—这个函数可能返回一个布尔值FALSE,但是也可能返回与FALSE等价的非布尔值,比如0或者""。更多信息可参看布尔值部分。在这个函数中使用===操作符来测试返回值。
最简单的方式是使用一个自定义图片来直接替换/misc/feed.png文件.如果不能这样做的话,你可以使用PHP来覆写默认的XML图标.
对于一个兼容PHPTemplate的主题,使用下面的步骤,你就可以轻松的将图标替换为你自己的(不仅仅是边栏区块中的,对于页面底部的也同样适用).本文基于如何覆写一个主题函数的步骤.
function phptemplate_feed_icon($url) {
$icon_url = 'path/to/new/icon';
if ($image = '<img src="'. $icon_url . '" alt="'. t('XML feed') .'" />') {
return '<span class="xml-icon"><a href="'. check_url($url) .'">'. $image .'</a></span>';
}
}
首先,将xtemplate.xtmpl改名为original.xtmpl,这样这个主题就不再是xtemplate主题了.
创建page.tpl.php
创建node.tpl.php
创建 comment.tpl.php
为了简洁起见,你可能想修改“new”的展示,只有当$new != ''时,才对其进行展示。
创建block.tpl.php
创建box.tpl.php
每当drupal更新换代的时候,就需要升级你的drupal主题,从而更好的利用新的特性,并保持与Drupal主题系统的兼容性。
Drupal7.x主题变动概述
区块的CSS ID更加人性化,语义更丰富
Drupal内核中定义的CSS ID,许多都已经被修改了,这使得它们的语义更加明确:
Block(区块) |
旧 CSS ID (Drupal 6) |
新 CSS ID (Drupal 7) |
Recent blog posts(最新博客) |
block-blog-0 |
block-blog-recent |
Book navigation(书籍导航) |
block-book-0 |
block-book-navigation |
Recent comments(最新评论) |
block-comment-0 |
block-comment-recent |
Active forum topics(热门帖子) |
block-forum-0 |
block-forum-active |
New forum topics(最新论坛帖子) |
block-forum-1 |
block-forum-new |
Language switcher(语言切换器) |
block-locale-0 |
block-locale-language-switcher |
Syndicate |
block-node-0 |
block-node-syndicate |
Most recent poll(最新投票) |
block-poll-0 |
block-poll-recent |
Author information(作者信息) |
block-profile-0 |
block-profile-author-information |
Search form(搜索表单) |
block-search-0 |
block-search-form |
Popular content(热门文章) |
block-statistics-0 |
block-statistics-popular |
Powered by Drupal(Drupal支持) |
block-system-0 |
block-system-powered-by |
User login(用户登录) |
block-user-0 |
block-user-login |
Navigation(导航) |
block-user-1 |
block-user-navigation |
Who's new(新进会员) |
block-user-2 |
block-user-new |
Who's online(在线会员) |
block-user-3 |
block-user-online |
例如,一个Drupal 6 的CSS样式如下:
/* Make the text in the user login block bigger. */
#block-user-0 {
font-size: 1.5em;
}
should become (in Drupal 7):
那么在Drupal 7中,应该变为:
/* Make the text in the user login block bigger. */
#block-user-login {
font-size: 1.5em;
}
一级和二级链接现在成了主菜单和次级菜单
一级和二级链接现在改名为了主菜单和次级菜单。对于使用到一级和二级链接的主题,在新的版本中,应该采用新的变量名:
Drupal 6.x
<div id="menu">
<?php if (isset($secondary_links)) { ?><?php print theme('links', $secondary_links, array('class' => 'links', 'id' => 'subnavlist')); ?><?php } ?>
<?php if (isset($primary_links)) { ?><?php print theme('links', $primary_links, array('class' => 'links', 'id' => 'navlist')) ?><?php } ?>
</div>
Drupal 7.x
<div id="menu">
<?php if (isset($secondary_menu)) { ?><?php print theme('links', $secondary_menu, array('class' => 'links', 'id' => 'subnavlist')); ?><?php } ?>
<?php if (isset($main_menu)) { ?><?php print theme('links', $main_menu, array('class' => 'links', 'id' => 'navlist')) ?><?php } ?>
</div>
对于Drupal6,有一个新的主题开发指南可用。
对于模块开发者,可参看该页:模块开发指南里面的使用主题层
Drupal 6.x主题变动概述
drupal主题现在也有了.info文件
在Drupal5.x中,模块中包含了.info文件,用来存储该模块的原数据(比如,名称,描述,版本,依赖性,等等)。从Drupal6.x开始,主题也包含了.info file。更多信息可参看为主题编写.info文件一文
例如themeName.info(只列出部分信息):
name = Theme Name
description = One sentence description of theme.
core = 6.x
engine = phptemplate
drupal主题注册表
现在所有的函数都需要注册。在Drupal 5中,是不需要注册的。在Drupal 6.x中,新引入了hook_theme()函数,用来注册所有的可主题化的输出。PHPTemplate引擎负责注册,所以大多数时候,你都不需要手工注册。
通过模板文件主题化
在Drupal 5.x中,可以使用themeEngine_hook() 或者themeName_hook()来覆写默认的主题函数。你可以直接在template.php文件使用标识文本(markup),并将结果数据返回。
你还有另一种选择,就是使用_phptemplate_callback(),这样就允许主题函数使用单独的模板文件了(.tpl.php)。启用回调函数以后还允许钩子使用下面的方式操纵变量。
下面的代码在6.x中已不再被支持了:
<?php
function _phptemplate_variables($hook, $variables) {
switch ($hook) {
case 'page':
// process variables for page hook.
break;
case 'node':
// process variables for node hook.
break;
}
return $variables;
}
?>
在Drupal 6.x中,已不再支持_phptemplate_callback()了。它已经被整合到theme()函数中了。一旦函数注册为了一个模板,那么就会使用该模板文件。_phptemplate_variables()也不再被支持了。为了在Drupal 6.x中添加额外的变量,可参看预处理文档。
如果以普通函数来注册的话,这种方式就和drupal5.x中使用"themeEngine_hook()" 或者"themeName_hook()"来覆写一样了。
PHPTemplate自动的将钩子注册为一个普通函数或者一个模板。为了将函数注册为一个模板,你需要根据钩子名,创建一个".tpl.php"文件。例如,假如钩子为menu_tree,那么你drupal主题中的模板文件名就应该为menu-tree.tpl.php.注意,下划线改为了连字符。清空注册表,现在就可以了。
模板管理
现在,可以将模板文件放到主题下面的子目录中,这样管理起来就更方便了。PHPTemplate引擎将查找主题目录下的所有文件,并将它们的位置登记器来。对于文件夹的嵌套深度则没有限制。
新的模板文件(.tpl.php)
在drupal5.x中,phptemplate.engine实现了下面的模板(在drupal题引擎文件夹下):
在Drupal6.x中,模板的应用更加广泛,现在默认模板文件更多了,而将来这个数量还会增加。Drupal5.x的PHPTemplate中的模板也被移走了。对于这些模板的用法,以及可用变量,你可以参看模板文件中的注释,里面有详细的说明。
为了覆写这些模板,你所要做的就是将它们拷贝到你的主题文件夹下,并清空主题注册表。
参看主题化手册中的新模板的完整列表。
一些很少使用的默认内核函数,由于模板能够起到同样的功能,所以这些函数被取消了。例如,theme_page就不存在了。这影响到了所有转化为模板的可主题化的输出。由于这种改变,是用另一种方式实现同样的功能,所有原有的实现(默认函数)就没有必要继续保留下来了。这一改变对用户没有任何影响。比如,现在你仍然可以使用theme('page'),虽然theme_page被取消了。这里所改变的仅仅是默认的实现。
新的模板命名建议
模板建议,对于page.tpl.php,是基于路径的;node.tpl.php是基于节点类型的;block.tpl.php是基于区域和模块的。对于前面提到的模板,同样也提供了相应的建议。
参看主题化手册中的新模板建议的完整列表。
定义区块区域
hook_regions已不再被支持了。现在可通过.info文件来定义区域。更多细节可参看手册页面。
regions[left] = Left sidebar
regions[right] = Right sidebar
regions[content] = Content
regions[header] = Header
regions[footer] = Footer
区块区域变量名的修改
边栏和页脚区块区域的变量名改变了。
在Drupal5.x中,page.tpl.php内部的"left", "right" 和"footer"区域所使用的变量为$sidebar_left, $sidebar_right 和 $footer_message。这种用法源于Drupal4.6及更早版本。
为了更加清晰直接,现在这三个区域的变量名分别为$left, $right 和$footer,这和其它区域保持了一致。$footer_message仍然可以使用,但它仅仅用于站点信息管理界面的的脚本信息。
自定义主题设置
主题作者制作的主题,现在更方便被站点管理员定制了。
在Drupal的管理界面,每个主题在admin/build/themes/settings/themeName下面都有自己的配置页面。这个页面中包含了一个表单,里面有标准的“Logo image settings” 和“Shortcut icon settings”等等。为了向这个表单添加其它的设置,在主题目录下面简单的创建一个theme-settings.php文件,并向其中添加一个themeName_settings() 或者themeEngineName_settings()函数。这个函数应该使用表单API来创建表单组件。
更多信息可参看主题开发指南手册的定制主题设置一文。
新的$signature变量
在Drupal6总,签名是动态的,这意味着当查看一个评论时才展示它们,而签名不再是评论本身的一部分了。因此,在comment.tpl.php中需要加一个$signature变量。
在Drupal5.x中:
<div class="content">
<?php print $content; ?>
</div>
在Drupal6.x中:
<div class="content">
<?php print $content ?>
<?php if ($signature): ?>
<div class="user-signature clear-block">
<?php print $signature ?>
</div>
<?php endif; ?>
</div>
注意:为了避免为旧的帖子展示两次签名,你可以这样:
<div class="content">
<?php print $content ?>
<?php if ($signature && $comment->cid > 1234): // Change "1234" to the last comment ID used before upgrading to Drupal 6 ?>
<div class="user-signature clear-block">
<?php print $signature ?>
</div>
<?php endif; ?>
</div>
$body_classes变量
在page.tpl.php中,可通过下面的方式得到布局的状态:
在Drupal5.x中,<?php print $layout; ?>将根据当前所用的边栏输出左栏(left),右栏(right),或者全部(both)。
在Drupal6.x中,还可以使用$body_classes。<?php print $body_classes; ?>将会输出类似下面的东西:
front logged-in node-type-page no-sidebars
它提供了如上面所示的CSS类。更多信息可参看:
http://drupal.org/node/171906
$language现在是一个对象了
PHPTemplate主题中的$language变量现在已不再是一个简单的保存当前浏览页面语言代码的字符串了,现在它是一个对象,包含了当前语言的多个属性。现在你可以为你的主题实现从右到左的兼容,这样你的主题就可以应用到从右到左的语言中了(比如希伯来语,其中http://www.drupal.org.il/就是一个很好的例子)
$language具有属性$language->language,用于当前语言的代码,而$language->direction则是一个整数(0(LANGUAGE_LTR)表示从左向右,1(LANGUAGE_RTL)表示从右向左)。如果你所关心的仅仅是升级你的主题,只需要将$language改为$language->language就可以了。
主题可以覆写内核或者模块定义的CSS文件了
通过使用drupal_add_css(),来添加一个同名的样式表,主题就可以替换模块定义的CSS文件了。这允许主题在需要的时候,覆写整个CSS文件,而不是特定的CSS选择器。
例如,如果将下面的代码放到Garland的template.php文件中,那么themes/garland/system-menus.css将会取代modules/system/system-menus.css:
<?php
drupal_add_css(path_to_theme() .'/system-menus.css', 'theme');
?>
支持RTL CSS覆写文件
为了更好的支持从右到左的语言,对于每个使用drupal_add_css()添加到页面模板的CSS文件来说,都可以拥有一个与之对应的从右到左的CSS文件。例如,style.css,在同一个目录下面,还可以包含一个style-rtl.css文件。style-rtl.css包含的就是对style.css中样式的覆写,这里只包含从右到左样式的覆写。Drupal内核为内置的模块和主题都包含了这样的RTL CSS文件。按照习惯,在原始CSS文件中要被覆写的CSS规则后面需要加上一个/* LTR */注释,这样当维护者需要修改CSS文件时,他就会注意到RTL CSS也需要修改。只有当前语言为RTL时,才会加载这些CSS文件。
摘自modules/system/defaults.css文件的一段:
th {
text-align: left; /* LTR */
padding-right: 1em; /* LTR */
border-bottom: 3px solid #ccc;
}
摘自modules/system/defaults-rtl.css文件的一段:
th {
text-align: right;
padding-right: inherit;
padding-left: 1em;
}
“Submitted by user on date/time”可被主题化
节点和评论的"submitted"元素成为了一个可以主题化的元素。这意味着你可以覆写要包含的信息,以及它的外观。
你可以添加自定义的id/class,你可以包含或多或少的信息,甚至根据节点或者评论类型的不同使用不同的外观。
实例,可参看garland主题的template.php文件。
jQuery升级到1.2.3
Drupal包含的jQuery JavaScript库已被升级到1.2.3版。
默认的JavaScript文件
与style.css类似,可以将script.js自动添加到主题的页面模板中。这个文件应放在主题的根目录下面。通过.info文件,可以修改这个文件名,以及添加更多的这类文件。
JavaScript主题化
现在在JavaScript代码中引入了一个主题化机制。和script.js的自动加载一起,这使得主题开发者在Drupal网页的脚本事件领域拥有了更大的灵活性。通常,JavaScript代码生成的HTML都是直接插入到页面中的。然而,这些HTML代码通常是硬编码到脚本中的,它不允许对插入的代码进行修改。
模块在Drupal.theme.prototype命名空间下提供了默认的主题函数。而主题应该直接将覆写函数放在Drupal.theme命名空间下。脚本将调用Drupal.theme('function_name', ...),由这个函数来决定决定究竟是采用主题提供的函数还是默认的函数。
JavaScript的主题函数,在返回类型上,是完全自由的。它可以是简单的字符串,也可以是复杂的数据类型,比如一个大对象,里面包含几个小的jQuery对象,而小对象里面又包含着DOM元素。自定义主题函数的返回类型,应该和默认主题函数保持一致。
相关的Drupal 5.0 API 变化
下面列出的是Drupal 5.0中与主题有关的API接口变化,如果你的主题中用到了下面的函数,都要进行相应的更新才能在5.0下正常工作:
$primary_links 与 $secondary_links 变量现在返回了结构化的链接
新增 drupal_add_css() 函数,用来添加CSS文件
新的 $feed_icon, 显示聚合图标
新的clearing类
theme_links() 改为返回链接列表
新增 drupal_add_js() 函数,用来添加javascript脚本
_phptemplate_callback 函数签名改变了
id='pager' 现在为 class='pager'
theme_form_element 的参数修改了。
详情请参看:http://drupalchina.org/node/1679
在Drupal的管理界面中,每个drupal主题都有自己的设置页面admin/build/themes/settings/themeName,在这里你可以配置标准设置比如“Logo image settings” 和“Shortcut icon settings”.
你可以使用本节所讲的方法,来为该页面表单添加额外的设置,从而完成对drupal主题的定制.
To add color.module support, see the Integrating with Color.module section.
为了添加color.module支持,可参看集成Color.module一节.
Steven Wittens写了一篇非常有名的文章“集成Color.module”,但是它对于许多Drupal开发者来说,有点抽象。所以,我想写点更具体的。
我还打算使用“主题化概述”里面的经常被忽视的一些信息,从而使升级更加容易一些。
创建你自己的drupal主题
现在,首先我不是一名drupal主题开发者。有很多人专注于drupal主题开发,但是我不行。我也只能拿着别人的东西修修补补。自己不能独立的创建自己的主题。
如果你和我一样的话,不要灰心。这里所讲的非常简单,至少相对简单。
首先,你可以直接在核心的"theme"文件夹下进行修改,但是这种方式不好,因为升级的时候会用到核心主题的,而drupal升级是经常性的,因为新版本在不断的发布,如果能不修改内核的话,就不修改内核。
所以,让我们先把核心主题拷贝到你的"sites/default/themes"或者"sites/all/themes"(取决于支持的站点数量)。这样你就有了核心主题的一个拷贝。
现在,你准备好创建一个自定义主题了么?这一技术同样适用于第3方主题,但是我们这里以Garland为例。
打开你的"sites/default/themes/garland"文件夹。在里面创建一个子文件夹。为你的新主题起个名字,比如让我们把它叫做"nancy"。
将"sites/default/themes/garland"文件夹下面的"screenshot.png" 和"styles.css"拷贝到"nancy"文件夹下。现在查看你的主题配置页面,现在就多出来了一个新主题"nancy"。漂亮吧?
为了修改Garland主题的颜色,你还需要将"color" 和"images"文件夹拷贝到你新主题文件夹下。
现在,你就可以放心的在这里进行修改了,这样就不用担心升级问题了。
提示:如果你的模块包含自己的CSS文件的话。不要直接修改这些CSS文件(因为模块的升级更加频繁)。你可以在你主题下面对模块样式进行覆写。
让它完全成为你的drupal主题
现在,让我们做一些更大的改动。我想逐步的来完成这一修改,每一步都可以产生显著的效果,这样你就可以跟着我学习了。
Garland的"style.css"(你拷贝的)里面包含了一个注释“Color Picker: don't touch”(颜色选择器:不要碰它)。找到这行,换行,在这里你将放置自己的改动。我建议你首先输入下面的内容:
/**************************************/
/*** My new stuff and overrides ***/
/**************************************/
还记得吧,Wittens先生曾说过第一个颜色方案是个参考颜色集。在一个基本安装中,这意味着是"Blue Lagoon"颜色集。它们的颜色为:
Name |
Color |
Base |
#0072b9 |
Link |
#027ac6 |
Header Top |
#2385c2 |
Header Bottom |
#5ab5ee |
Text |
#494949 |
这意味着,如果你有了新的选择的话,"color picker"(颜色选择器)将会使用它来替换这5个值。注意:我不知道这是不是一个bug,但是根据我的经验,你实际上用不到这个“基”颜色集(参看,在生成的样式表中删除基颜色)。
好了,让我们做个试验。请确定你已经将新主题设为了drupal默认主题了。
这里要做的就是为了,让读者更好的浏览你的站点。一个关键的建议就是让你的边栏元素和主内容区域区别开来。好的,让我们试着做一下:我们把我们所有的区块都使用Garland的 "header top"颜色作为背景色。记住,它的参考颜色为"#2385c2"。
.block {
background-color: #2385c2;
}
保存修改后的"style.css",并点击“保存设置”按钮。简单吧!所有区块的背景颜色都变了。
选择一个不同的颜色集。区块就会随着改变。很漂亮吧,不是么?
美中不足的是,区块中的文本的可读性不够强。你可能会认为,只需要在这里加行代码就可以了,比如color: #ffffff;(这可能对于基颜色还说得过去)。不过没有这么简单,除非用的都是最简单的区块。
像导航这样的区块,在它里面都使用了更多的样式名字。主要有"menu" 和"leaf."所以我们需要添加几行:
.block {
background-color: #2385c2;
color: #ffffff;
}
.block li.leaf a {
color: #ffffff;
}
不错!基本上差不多了。但是它还没有涉及到可扩展的元素。因为,这些元素使用了不同的名字:
.block {
background-color: #2385c2;
color: #ffffff;
}
.block li.leaf a {
color: #ffffff;
}
.block li.collapsed a {
color: #ffffff;
}
.block li.expanded a {
color: #ffffff;
}
好了!我的Drupal站点看起来比过去强多了。
现在,让我们加点更有趣的。比如,这些区块的边界?让我们选择颜色选择器的"link"颜色。它的参考颜色为"#027ac6."
.block {
background-color: #2385c2;
border: 3px ridge #027ac6;
color: #ffffff;
}
.block li.leaf a {
color: #ffffff;
}
.block li.collapsed a {
color: #ffffff;
}
.block li.expanded a {
color: #ffffff;
}
精彩吧!好了,让我们做最后一个修改,其它都留给你自己发挥了。
我想让book导航更突出一点,这样就能吸引更多的眼球了。在前面,我们为所有的区块设置了背景色,那么我们可不可以为book导航区块的背景颜色设为"header bottom"颜色呢?它的参考颜色为"#5ab5ee."
.block {
background-color: #2385c2;
border: 3px ridge #027ac6;
color: #ffffff;
}
.block li.leaf a {
color: #ffffff;
}
.block li.collapsed a {
color: #ffffff;
}
.block li.expanded a {
color: #ffffff;
}
#block-book-0 {
background-color: #5ab5ee;
}
现在你站点的外观更与众不同了,而且你还学会了如何使用参考颜色来自动的保持站点颜色的统一。尽管这里只使用了4种颜色,但是却展示不少的CSS技巧。好玩吧。
下面是对上面的总结。
/* Blocks */
.block {
border:3px ridge #2385c2;
background-color: #5ab5ee;
color: #ffffff;
font-size: 1.00em;
line-height: 100%;
}
.block .leaf {
color: #ffffff;
}
.block .leaf a {
color: #ffffff;
}
.block li.collapsed a {
color: #ffffff;
}
.block .expanded {
background: #2385c2;
color: #ffffff;
}
.block .expanded a {
background: #2385c2;
color: #ffffff;
}
.block .active {
border:2px outset #027ac6;
color: #ffffff;
}
/* Books - reverse scheme */
#block-book-0 {
border:3px ridge #5ab5ee;
background-color: #2385c2;
color: #ffffff;
font-size: 1.00em;
line-height: 110%;
}
#block-book-0 .active {
border:2px outset #0072b9;
}
/* Special blocks */
#block-event-1 a {
color: #ffffff;
}
#block-event-1 .more-link a {
padding-right: 50px;
font-weight: bold;
}
其它
也不费太多功夫了,我在这里列出其它的颜色集,仅作参考:
$info = array(
// Pre-defined color schemes
// base, link, header top, header bottom, text => name
'schemes' => array(
'#0072b9,#027ac6,#2385c2,#5ab5ee,#494949' => t('Blue Lagoon (Default)'),
'#464849,#2f416f,#2a2b2d,#5d6779,#494949' => t('Ash'),
'#55c0e2,#000000,#085360,#007e94,#696969' => t('Aquamarine'),
'#d5b048,#6c420e,#331900,#971702,#494949' => t('Belgian Chocolate'),
'#3f3f3f,#336699,#6598cb,#6598cb,#000000' => t('Bluemarine'),
'#d0cb9a,#917803,#efde01,#e6fb2d,#494949' => t('Citrus Blast'),
'#0f005c,#434f8c,#4d91ff,#1a1575,#000000' => t('Cold Day'),
'#202020,#0633a7,#000000,#808080,#000000' => t('Gothic'),
'#c9c497,#0c7a00,#03961e,#7be000,#494949' => t('Greenbeam'),
'#cf68a2,#c71a72,#fbbcde,#d84b7e,#9b3b3b' => t('In The Pink'),
'#ffe23d,#a9290a,#fc6d1d,#a30f42,#494949' => t('Mediterrano'),
'#788597,#3f728d,#a9adbc,#d4d4d4,#707070' => t('Mercury'),
'#5b5fa9,#5b5faa,#0a2352,#9fa8d5,#494949' => t('Nocturnal'),
'#7db323,#6a9915,#b5d52a,#7db323,#191a19' => t('Olivia'),
'#12020b,#1b1a13,#f391c6,#f41063,#898080' => t('Pink Plastic'),
'#d48440,#d20404,#a1443a,#f6352c,#871d12' => t('Raging Bull'),
'#29996a,#1f563f,#0ce4cc,#02ca90,#2a8d8a' => t('Seascape'),
'#b7a0ba,#c70000,#a1443a,#f21107,#515d52' => t('Shiny Tomato'),
'#18583d,#1b5f42,#34775a,#52bf90,#2d2d2d' => t('Teal Top'),
'#6b91ff,#c97b26,#fdb9d5,#fa6196,#1a1919' => t('Victorian'),
'#ffee00,#3e8bb1,#f6e304,#91fd0d,#419d39' => t('Wake Up Call'),
'#f1db09,#bd8a05,#fbf498,#faec14,#6e4308' => t('Yellow Pages'),
),
很多Drupal资料都讲解了,如何通过覆写主题函数,来真正的完成定义,从而得到你想要的外观.由于你需要创建一个函数,所以它需要一点PHP知识,尽管如此,这样做还是非常强大的.这里有许多代码片断和例子可供选择,许多情况下你只需要拷贝粘贴并做些细小的改动就可以了.
使用哪个文件
为了使用drupal主题覆写,首先要做的就是找个地方来放置覆写函数.这取决于你所使用的主题引擎,引擎不同,放置的位置也不同.在Drupal 4.6中默认的引擎为XTemplate;然而XTemplate在PHP5下面不能好好的工作,所以从Drupal 4.7以及更高的版本现在都使用PHPTemplate作为默认的主题引擎了。
你的文件应该放在你主题目录themes/MYTHEMENAME下面。
Plain PHP Theme(纯PHP主题)
如果使用的是纯PHP主题,比如Marvin 或者Chameleon,放置覆写函数的文件应该是THEMENAME.theme。
XTemplate
XTemplate不允许主题函数覆写。
PHPTemplate
PHPTemplates将主题覆写放在了template.php中。
SmartyTemplates
SmartyTemplates使用smartytemplate.php文件。
wgSmarty
wgSmarty使用template.php,但是在编写本文时,相关代码已被注掉;现在,在wgSmarty中也不能进行主题覆写了。
phptal
PHPTAL使用template.php,它与PHPTemplate非常相似。
如果你的主题目录里面还没有template.php文件的话,你需要创建一个。创建这个文件时,开头一定要包含一个“<?php”,否则的话,你可能会遇到站点被搞死的现象。后面的结束标签,则没有需要。
如何给覆写函数命名
当你覆写一个drupal主题函数时,模块文档一般都会给出相关的说明的,覆写哪个,如何覆写。为了覆写drupal主题函数,你需要根据原有函数创建一个略有不同的函数,这是由于在PHP中,函数名必须唯一的缘故。
我们这里使用的例子函数为theme_item_list,每当Drupal要展示一列项目时,就会调用这个函数,在Drupal中,除了在菜单结构以外,每当你遇到<li>标签时,基本上就是用的这个函数。
函数的签名为:
<?php
function theme_item_list($items = array(), $title = NULL, $type = 'ul') {
?>
覆写的命名方式有两种,除非你使用的是纯PHP引擎。第一种方式,可以将函数名前面的'theme_'替换为'MYTHEMENAME_',来为覆写函数命名。如果你用的是chameleon模板的话,在你的chamelon.theme文件中,可以这样覆写theme_item_list函数:
<?php
function chameleon_item_list($items = array(), $title = NULL, $type = 'ul') {
?>
这实际上不是最好的,但是它总是可以工作的。大多数drupal主题,都是缘于模板引擎的。所以他们可以使用drupal模板引擎的名字来代替主题名字。这是推荐的方式,因为这可以使得使用不同主题的用户共享这个函数。甚至可以在同一个站点上让多个主题共享一个包含这些函数的文件。
一些例子:
<?php
function phptemplate_item_list($items = array(), $title = NULL, $type = 'ul')
function smarty_item_list($items = array(), $title = NULL, $type = 'ul')
?>
导言
很多人都知道如何覆写一个标准的theme_函数,但是对于表单主题的覆写,可能就不大清楚了,在Drupal 5.x(及更高版本)中由FormAPI生成的表单,可用相似的方式来定制其主题.本文将一步步给出表单主题化的流程.
样例表单和数据
经常会有人问,如何在一个表格(table)内部嵌入复选框。我们这里就讲讲这个。首先我们要为表格准备点数据,这些数据将会和复选框一起放到表格中。所以,我们先这样:
<?php
$r = db_rewrite_sql(db_query("SELECT nid, title, created FROM {node} LIMIT 20"));
while ($row = db_fetch_object($r)) {
$rows[] = $row;
}
?>
注意,我们这里使用了一个SQL LIMIT,这样就不会取出全部数据了,如果将来讨论分页的话,就会去掉这个关键字。还有,很明显,在drupal中,这也不是获取节点的最好方式。这里仅仅是个例子,用来说明问题的。
现在,使用这些数据,加上复选框,我们就可以创建一个表格了,我们将把复选框放在最左边,这和Drupal内核中的批量操作非常类似。
首先要做的是生成$form数组,以供drupal_get_form来调用。
请看下面这段代码:
<?php
$r = db_rewrite_sql(db_query('SELECT nid, title, created FROM {node} LIMIT 20'));
while ($row = db_fetch_object($r)) {
$rows[] = $row;
}
foreach ($rows as $v) {
$nids[$v->nid] = '';
$form['title'][$v->nid] = array('#type' => 'markup', '#value' => check_plain($v->title));
$form['created'][$v->nid] = array('#type' => 'markup', '#value' => date("d/m/Y H:i", $v->created));
}
$form['nids'] = array('#type' => 'checkboxes', '#options' => $nids);
?>
这看起来可能有点特别。大多数表单都是静态的,开发者将会使用一列长长的$form声明。而在这里,我们看到的表单却是动态的。在循环的内部,我们创建了两个markup类型的$form['title'][$v->nid] 和$form['created'][$v->nid]数组。它用来在表单内部展示这些字段。$nids[$v->nid]是一个数组,将传递给表单元素checkboxes的options。
最后每个表格都需要一个头部(header)。由于没有其它更好的方式,所以我们需要把头部数组作为表单的一部分传递过来。所以,我们将添加一个表格头部数组,这里还使用markup类型,最后我们将调用drupal_get_form()来得到表单本身。
<?php
$r = db_rewrite_sql(db_query('SELECT nid, title, created FROM {node} LIMIT 20'));
while ($row = db_fetch_object($r)) {
$rows[] = $row;
}
foreach ($rows as $v) {
$nids[$v->nid] = '';
$form['title'][$v->nid] = array('#type' => 'markup', '#value' => check_plain($v->title));
$form['created'][$v->nid] = array('#type' => 'markup', '#value' => date("d/m/Y H:i", $v->created));
}
$form['nids'] = array('#type' => 'checkboxes', '#options' => $nids);
$form['submit'] = array('#type' => 'submit', '#value' => 'Update');
$form['header'] = array(
'#type' => 'value',
'#value' => array(
array('data' => 'nid'),
array('data' => t('Title')),
array('data' => t('Created')),
)
);
return drupal_get_form('my_test_form', $form); // 4.7
?>
现在,如果我们啥都不再做的话,打开浏览器,你也会看到结果,不过样子丑了点(在测试站点上进行试验)。所以,现在我们要做的就是把这个表单放在一个表格中,我们这里使用了一个主题覆写函数。
这个函数的名字,源于上面传递给drupal_get_form()的$form_id参数。所在,在这里,函数名就是theme_my_test_form($form)。下面就是这个函数:
<?php
function theme_my_test_form($form) {
foreach (element_children($form['title']) as $key) {
$row = array();
$row['data'][0] = form_render($form['nids'][$key]);
$row['data'][1] = form_render($form['title'][$key]);
$row['data'][2] = form_render($form['created'][$key]);
$rows[] = $row;
}
$output = form_render(theme('table', $form['header']['#value'], $rows));
$output.= form_render($form); // Process any other fields and display them
return $output;
}
?>
Drupal 5用户应该使用drupal_render()来代替这里的form_render()。
那么,这个函数都干了些什么呢。首先,将$form数组传递给了这个主题函数。这个数组中包含了各种东西,submit(提交), form_token,甚至表格头部也在这里。为了提取里面的数据记录,我们在这里使用element_children()函数得到一个子数组(我选择了$form['title']),然后对其循环处理。
你可以看到,每行有3个<td> </td>,最左边的为复选框,后两个为markup字段。现在,运行这段代码,你就会看到每条数据库记录都会显示在一个表格行中,而复选框位于最左边。
我将在以后的教程中,进一步讲解相关技术,比如表格的排序,以及对大的数据集进行分页。
感谢a79v对本文的帮助。
在Drupal社区中,PHP Template引擎是默认的标准,大部分人都使用这种引擎,除了这个以外,还有其它的一些引擎可供选择。
译者注:
XTemplate在drupal4.7种已经被移出了内核了,而且现在已经停止了对这个引擎的维护,可以说是完全绝迹了。对本文的翻译仅仅是把它作为一个参考,让大家了解一下drupal4.6以前,人们是如何制作drupal主题的。作为以前的drupal默认主题引擎,XTemplate的许多优点都被引入到了PHPTemplate中去了。
而smarty模板,是目前PHP中的一个主流模板,许多PHP开发者对此应该比较熟悉,所以,也加了进来。
还有,部分内容确实一点价值都没有了,所以没有翻译,还有就是http://drupal.org/node/220789 一文,里面讲的是如何使用photoshop,虽然与主题相关,但是没有涉及到drupal的知识,我对photoshop不大理解,所以也没有翻译。
XTemplate主题系统使用模板来控制网页的布局和样式.它将逻辑(PHP)、结构(XHTML/HTML)、和样式(CSS)独立开来,对于设计者来说,只需要使用XHTML/HTML和CSS就可以创建或者修改模板了,而不用担心PHP代码。
XTemplate模板就是一些目录,这里面包含了模板用到的所有的XHTML/HTML,CSS,图片和JavaScript文件。模板位于Drupal安装目录下面的themes目录里:
/themes/
一旦将一个模板放到themes目录下,XTemplate就会自动找到它,并将它展示到后台管理的主题选择页面:
administer -> themes
Drupal发布时带了两个XTemplate模板Bluemarine 和Pushbutton。
尽管XTemplate现在还是内核的一部分,但是将来它将被移出内核了,这有多个原因。这并不意味着XTemplate就不再开发了,它将作为一个第3方引擎,可在drupal中继续使用。
为了创建一个新的XTemplate,你需要在Drupal的/themes/目录下面创建一个子目录.
而子目录的名字,就会作为你的新模板的名字,对于
/themes/rembrant
一旦你在这个目录创建一个模板的话, "rembrant"模板就会出现在主题选择页面.
创建一个新模板的最简单的方式就是,拷贝一个已存在的模板,比如默认的Pushbutton,然后对拷贝的文件进行修改.
在一个模板目录下面,唯一必须的文件就是xtemplate.xtmpl,它是一个规则的HTML或者XHTML文件,里面包含了一些XTemplate标签,在一个页面展示时,drupal会使用内容来替换这些标签。你可以使用多种编辑器来编辑xtemplate.xtmpl文件,比如DreamWeaver, GoLive, BBEdit或者其它你使用的HTML/XHTML编辑器。
所有的其它文件都是可选的,在xtemplate.xtmpl文件中存在这些文件的链接。其中可以包括CSS、图片、JavaScript文件,这些文件也都应该放在模板目录下面,这样便于Drupal的维护和迁移。
注意,如果你将你的样式表命名为style.css的话,drupal将会自动加载它,而你就不需要为它添加一个@import 或者 <link />语句了。如果在你的模板目录下面还有一个子目录,里面包含了一个style.css文件的话,那么这个子目录将成为一个新的主题,它将使用父目录中的XHTML模板,但是将采用子目录里面的样式。
xTemplate是这样生成网页的,使用从数据库中取出的内容,替换xtemplate.xtmpl模板文件中的占位标签,从而生成相应的页面的.
有两种类型的模板占位标签, section标签,和项目(item)标签
Section(片断)标签
Section标签用来处理网页的结构,标记页面的区域,实际就是XHTML/HTML注释标签,样子如下所示:
<!-- BEGIN: title -->
<!-- END: title -->
一些section标签标记的是内容,而它的结构可以重复.例如评论部分可以重复多次,这取决于一个页面的评论数量
<!-- BEGIN: comment -->
<!-- END: comment -->
Section标签还可以嵌套,这样一个section标签,可以包含其它的section标签:
<!-- BEGIN: node -->
<!-- BEGIN: title -->
<!-- END: title -->
<!-- END: node -->
项目标签
项目标签实际上是内容项的展位符,可以页面的标题,作者,或者是页面的主内容.项标签是这样的:
{title}
{submitted}
{content}
项标签外面需要使用section标签,例如:
<!-- BEGIN: node -->
{title}
<!-- END: node -->
前面的{title}标签,是页面的主标题,而下面的{title}标签则是页面评论的标题.
<!-- BEGIN: comment -->
{title}
<!-- END: comment -->
头部
xTemplate头部的开始和结束标签如下:
<!-- BEGIN: header -->
<!-- END: header -->
不要将这里的头部与XHTML/HTML的<head>元素混淆了.头部里面不但包含了<head>元素,它还包含了网页的顶部---设计者通常称之为“页首”,它里面通常包含一个带有站点logo和导航链接的横幅。
Prolog(序言)
WC3建议,所有的XHTML文档都应该以一个XML Prolog(序言)开始,用来声明文档的编码,例如:
<?xml version="1.0" encoding="utf-8"?>
不幸的是,许多浏览器对XML序言的支持有限,页面或者完全显示不了,或者显示的不正确。因此推荐大家不用XML序言,换种方式,可以在你模板的<head>中的Content-Type元素里声明编码。
DOCTYPE
DOCTYPE元素告诉浏览器两件事情,1,文档所使用的XML语言,2,该语言的DTD(文档类型声明)所在的位置。
下面是一个DOCTYPE元素的例子:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
在DOCTYPE或者XML序言前面,不应该有任何东西了。不过可以加上xTemplate的<!-- BEGIN: header -->标签,因为在页面发送给浏览器以前,Drupal将把这个标签删除掉,但是记住这个标签和DOCTYPE或者XML序言之间,不能有空格或者换行符,否则的话,你可能会得到意想不到的结果。
关于DOCTYPE元素的更多细节,以及你要使用哪个版本,参看:为你的站点使用正确的DOCTYPE!作者Jeffrey Zeldman
{head_title}
<title>元素的内容。用作浏览器的视窗标题,也作为搜索引擎里面的页面标题。
{head}
向里面填充以下内容:
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<base href="http://example.com/" />
<style type="text/css" media="all">
@import url(misc/drupal.css);
</style>
{styles}
声明当前的样式:
<style type="text/css" media="all">@import "themes/bluemarine/style.css";</style>
通过添加这个标签,你的 模板就可以利用Drupal主题系统的样式切换功能了。注意,如果你有一个默认的样式表的话,它的名字应为style.css并且和你的xtemplate.xtmpl文件位于同一个目录下面。
{onload_attributes}
<body>标签的页面属性。
{logo}
Logo部分的开始和结束标签如下:
<!-- BEGIN: logo -->
<!-- END: logo -->
站点logo的文件名,可以在Drupal主题管理界面中进行配置。(该项的展示是可选的)
{site_name}
站名部分的开始和结束标签如下:
<!-- BEGIN: site_name -->
<!-- END: site_name -->
当前的站名,可在drupal管理界面:administer->settings中进行配置。
(该项的展示是可选的)
{site_slogan}
站点标语部分的开始和结束标签如下:
<!-- BEGIN: site_slogan -->
<!-- END: site_slogan -->
当前的站点标语,可在drupal管理界面:administer->settings中进行配置。(该项是可选的)。
{secondary_links} {primary_links}
在Drupal主题管理界面中,通过设置"Secondary links"(二级链接) 和"Primary links"(一级链接),你就为这两个标签设置了内容。如果管理员没有声明任何"Primary links"(一级链接)的话,Drupal将根据当前启用的模块来自动生成一组链接。
管理员可以使用这些标签来输入指向站点主区域的链接,包括站点的标题,站点消息,图片或者其它需要的东西。
Search Box (搜索框)
搜索框的开始和结束标签如下:
<!-- BEGIN: search_box -->
<!-- END: search_box -->
{search_url}
表单action: "search"
{search_description}
搜索框中的替代文本说明,比如“输入你要搜索的词语”。
{search_button_text}
搜索提交按钮上的值:“搜索”。
Mission(使命)
Mission的开始和结束标签如下:
<!-- BEGIN: mission -->
<!-- END: mission -->
{mission}
站点使命,仅出现在首页,可在drupal管理界面:administer->settings中进行设置。
Title(标题)
标题的开始和结束标签如下:
<!-- BEGIN: title -->
<!-- END: title -->
{title}
节点的标题
Tabs(标签集)
标签集的开始和结束标签如下:
<!-- BEGIN: tabs -->
<!-- END: tabs -->
{tabs}
当前页面的Drupal“本地任务”标签。
{breadcrumb}
页面的面包屑,是从首页通往当前页面的路径。
Help(帮助)
帮助的开始和结束标签如下:
<!-- BEGIN: help -->
<!-- END: help -->
{help}
包含了用于特定页面的任何帮助信息。
Message(消息)
消息的开始和结束标签如下:
<!-- BEGIN: message -->
<!-- END: message -->
当用户完成了某项操作,Drupal确认其操作结果时,就会显示消息,例如,更新或者删除一个页面后,都会在顶部出现几行消息。
{message}
消息的文本。
注意:这个可用于Drupal 4.6.5;它应该也适用于Drupal4.6.6
XTemplate的一个不足是,当它展示一级链接时,不能够处理里面的PHP.不过,我从php.net的用户评论中,找到两个函数,可以帮助XTemplate引擎解决这个问题:
function eval_mixed_helper($arr){
return ("echo stripslashes(\"".addslashes($arr[1])."\");");
}
function eval_mixed($string){
$string = "<? ?>".$string."<? ?>";
$string = preg_replace("/<\?=\s+(.*?)\s+\?>/", "<? echo $1; ?>", $string);
$string = str_replace('?>', '', str_replace( array(' <?php', '<?'), ', preg_replace_callback( "/\?> ((.|\n)*?)<\?(php)?/","eval_mixed_helper",$string) ) );
return eval($string);
}
?>
$xtemplate->template->assign(array(
"language" => $GLOBALS['locale'],
"head_title" => implode(' | ', $head_title),
"head" => drupal_get_html_head(),
"styles" => theme_get_styles(),
"onload_attributes" => theme_onload_attribute(),
"primary_links" => theme_get_setting('primary_links'),
"secondary_links" => theme_get_setting('secondary_links')
));
替换为下面的代码:
// allow for php in the primary links
ob_start();
eval_mixed(theme_get_setting('primary_links'));
$primary_link_eval = ob_get_clean();
$xtemplate->template->assign(array(
"language" => $GLOBALS['locale'],
"head_title" => implode(' | ', $head_title),
"head" => drupal_get_html_head(),
"styles" => theme_get_styles(),
"onload_attributes" => theme_onload_attribute(),
"primary_links" => $primary_link_eval,
"secondary_links" => theme_get_setting('secondary_links')
));
?></li>
现在,就可以在你的一级链接中添加php代码了。
在drupal4.7中,XTemplate已被移出了内核。现在,它成为了一个第3方模块了。
XTemplate作为第3方模块仅仅开发了一个4.7版本,当drupal升级到5.x的时候,就停止了维护,正式退出了历史舞台。
节点部分
xtemplate.xtmpl中的节点部分包含了页面的主内容,它的开始和结束标签如下:
<!-- BEGIN: node -->
<!-- END: node -->
{sticky}
如果节点被"stickied"(粘贴)到了列表的顶部(比如页面的teaser总是展示在首页的顶部),那么将css class(类)设为"node sticky"。如果节点没有被设为sticky的话,那么css class(类)设为"node "。
Picture 头像
头像就是代表节点作者的小图片,它将链接到作者的个人介绍页面上去。头像的开始和结束标签如下:
<!-- BEGIN: picture -->
<!-- END: picture -->
{picture}
比如可以输出以下内容:
<a href="user/1" title="View user profile.">
<img src="http://www.yoursite/files/pictures/picture-1.gif" alt="Username's picture" /></a>
Title(标题)
节点的标题,它的开始和结束标签如下:
<!-- BEGIN: title -->
<!-- END: title -->
在节点页面,标题的输出为:
<h1 class="title">Node Title</h1>
在drupal首页,每个节点标题的输出为:
<h2 class="title"><a href="node/31">Node Title</a></h2>
{link}
输出指向节点的链接,例如上面的"node/31"。
{title}
输出节点标题的文本,例如,上面的"Node Title"就是一个很好的例子。
{submitted}
节点的作者,发表时间等等,输出为:
Submitted by <a href="user/1" title="View user profile.">Username</a> on
Taxonomy(分类)
节点所属的分类的链接,可以包含多个分类,它的开始和结束标签如下:
<!-- BEGIN: taxonomy -->
<!-- END: taxonomy -->
{taxonomy}
输出一个节点所属的分类术语(term):
<a href="taxonomy/term/30">Taxonomy Term</a>
{content}
节点的主内容。
Links(链接)
节点的一些可选项比如:"printer-friendly version"(适合打印的版本), "add new comment"(添加新评论),以及用户对节点的访问记录。它的开始和结束标签如下:
<!-- BEGIN: links -->
<!-- END: links -->
{links}
输出如下(取决于用户的权限):
<a href="book/print/8" title="Show a printer-friendly version of this book page and its sub-pages.">printer-friendly version</a> | <a href="comment/reply/8#comment" title="Share your thoughts and opinions related to this posting.">add new comment</a> | <a href="admin/statistics/log/node/8">662 reads</a>
评论部分
xtemplate.xtmpl的评论部分包含了与节点相连的所有评论.它的开始和结束标签如下:
<!-- BEGIN: comment -->
<!-- END: comment -->
这部分仅为节点创建一个评论部分,当有多个评论时,将会自动重复调用这部分模板.
Avatar(头像)
头像包含了一个小图片,用来代表节点评论者,图片将链接到评论者的个人介绍页面. 它的开始和结束标签如下:
<!-- BEGIN: avatar -->
<!-- END: avatar -->
{avatar}
输出如下所示:
<div class="avatar">
<a href="user/1" title="View user profile.">
<img src="http://www.drupal.site/files/avatars/avatar-1.jpg" alt="username's avatar" />
</a>
</div>
Title(标题)
评论点标题. 它的开始和结束标签如下:
<!-- BEGIN: title -->
<!-- END: title -->
{link}
如果需要的话,可以在评论标题上加个指向评论的链接.在特定的一些drupal站点的评论视图中,会用到它.
{title}
评论标题的文本.
Submitted(提交信息)
{submitted}
用来展示评论者的名字,链接到他们的个人页面,以及评论发布的日期时间.下面是个输出样例:
Submitted by <a href="user/10" title="View user profile.">username</a> on Mon,
New(新)
用来指示评论是不是最新的. 它的开始和结束标签如下:
<!-- BEGIN: new -->
<!-- END: new -->
{new}
向一个评论中添加一个单词“新”
Content(内容)
展示评论的内容。
{content}
评论的文本。
Links(链接)
评论操作的控制链接,比如“回复”,“删除”,“编辑”。它的开始和结束标签如下:
<!-- BEGIN: links -->
<!-- END: links -->
{links}
展示控制链接。
区块
区块部分包含了左右栏,可用来展示各种各样的导航、特性选项等,比如论坛主题,博客,在线会员,以及RSS种子链接。通过配置,可以将区块放在页面的左栏或者右栏,或者两个都放。它的开始和结束标签为:
<!-- BEGIN: blocks -->
<!-- END: blocks -->
{blocks}
可以通过drupal的管理员界面(admin/system/block),来配置这里展示的内容。
Block(区块)
单个区块定义了每个区块的结构,注意这里没有's', block/blocks.
<!-- BEGIN: block -->
<!-- END: block -->
{module}
被展示区块所属的drupal模块的名字,这将添加到CSS class 和ID中去,可用于定制区块的外观
{delta}
区块在模块中的序列号,如果一个drupal模块生成多个区块的话,那么每个区块都有一个唯一的ID
{title}
区块的标题
{content}
区块的内容。
页脚部分
页脚部分出现在每个页面的最底部,通过drupal管理员界面(admin/settings)可以声明它的内容。它的开始和结束标签如下:
<!-- BEGIN: footer -->
<!-- END: footer -->
Message(消息)
这个区域用来装饰管理员发布的消息的,方法是通过在消息外面加点html标识字体。它的开始和结束标签如下:
<!-- BEGIN: message -->
<!-- END: message -->
{footer_message}
展示"Footer message"字段中定义的实际内容,可在drupal管理员界面(admin/settings)中设置这个字段。
{footer}
输出由Drupal模块生成的页脚消息(比如,devel.module的性能统计信息)
设置
为了使用Adobe GoLive来编辑xTemplate模板文件(xtemplate.xtmpl),你需要按照下面步骤先进行设置:
编辑
当打开一个模板文件时, GoLive将会询问你要使用哪种编码,此时选择"UTF-8".
如果你打开一个模板后,只能看到"body onload-attributes"的话,你需要进入源代码模式,并将"{onload_attributes}"从<body{onload_attributes}>中删除.
记住,当编辑完成以后,把"{onload-attributes}"重新加上来.
在xtemplate.xtmpl中,你可能希望临时的添加下面一行:
<link type="text/css" rel="stylesheet" href="style.css" />
记住,在完成编辑以后,一定要把这一行删掉.如果你忘了的话,那么drupal就不能为你的主题切换样式了。如果style.css存在的话,drupal会将其自动加载到{styles}标签中。
PHP主题是Drupal主题化的最直接的方式了。一个PHP主题包含了对Drupal内置主题函数的覆写。大多数情况下,你只需要覆写基本的主题钩子(比如,页面,节点,区块等等),但是你还可以覆写你想要的任何东西,比如列表和链接的主题。
为了创建一个PHP主题,你需要在你的themes目录下面创建一个子目录(我们这里假定为themes/mytheme),在子目录中,我们创建一个mytheme.theme文件。这个文件是普通的PHP文件,所以一定要包含<?php ?>标签。
在Drupal中,默认的主题函数的命名方式为theme_something() 或者theme_module_something(),后者允许模块为Drupal添加可以主题化的默认外观。一些基本的主题函数,比如theme_error() 和theme_table(),从它们的名字我们就可以看出,它们分别返回一个错误消息和一个表格的HTML代码。由模块定义的主题函数,包括theme_forum_display() 和theme_node_list()。
在你的.theme文件中,你可以覆写任何主题函数。为了覆写主题函数theme_something(),你需要在你的.theme文件中定义一个mytheme_something()函数。这个函数应该和原有函数保持一致。你可以将原有函数拷贝过来,重命名函数名,然后按照需求进行修改。为了避免在将来升级Drupal时出现问题,最好标出原始drupal函数与你定制版本的区别。这样当原有版本被修改时,你能方便的跟进。
必须的函数
除了这些主题函数以外,你还需要两个函数。
第一个就是mytheme_features()。这个函数返回以恶字符串数组,用来标记你主题所支持的特性(比如,搜索框,logo,使命(mission)…)。主题系统会在管理员界面提供这些特性的复选框和相应的设置。在你的代码中,你可以使用theme_get_setting()来获取这些设置的值。如果你打算把你的主题提交到drupal.org上的话,那么建议你实现所有的drupal特性,这样他人就可以定制你的主题了。
可用特性有:
logo |
可用的logo。主题应该检查以下设置:default_logo (布尔值) 和logo_path (字符串) |
toggle_logo |
Logo可被开启和关闭 |
toggle_name |
站名可被开启和关闭 |
toggle_search |
搜索框可被开启和关闭 |
toggle_slogan |
站点标语可被开启和关闭 |
toggle_mission |
站点使命可被开启和关闭 |
toggle_node_user_picture |
展示挨着节点的用户图片,可选的 |
toggle_comment_user_picture |
展示挨着评论的用户图片,可选的 |
下面是标准的chameleon.theme中的_features()函数:
<?php
function chameleon_features() {
return array(
'logo',
'toggle_name',
'toggle_slogan');
}
?>
第2个必须的函数是mytheme_regions(),它定义了主题中可用的区域。
下面是chameleon.theme的_regions()函数:
<?php
function chameleon_regions() {
return array(
'left' => t('left sidebar'),
'right' => t('right sidebar')
);
}
?>
对于你定义的每个区域,你可能想包含一个代码片断,用来在页面中输出该区域的内容。一般情况下,代码是这个样子的:
<?php
if ($blocks = theme_blocks('regionname')) {
$output .= '[wrapping content]' . $blocks . '[/wrapping content]';
}
?>
注意这里的'regionname'是数组中的键,而不是它的文本描述(比如,使用'left',而不是'left sidebar')。这里的'wrapping content'(包装内容),是用来在你的区域内容外面添加一些div, td,文本,或者其它元素的。
所以,为了添加额外的区域,你需要 (a)将你的区域添加到你的_regions()函数的区域列表中, (b)如上所示,将结果内容输出到页面中去。这里的代码简洁易懂,不妨尝试一下。
目录名称
注意,于模板和样式不同,主题是和它们的目录名称绑定到一起的。如果你想复制一个主题,你需要对它的目录,.theme文件,以及.theme文件中的函数名,进行重命名。
Smarty主题引擎由Travis Cline维护,它可以在Drupal主题中方便的使用Smarty模板引擎语法.它是从PHPTemplate主题引擎移植过来的.
正是由于它是移植过来的,所以大多数情况下, PHPTemplate的文档都可用来作为参考.
与PHPTemplate类似,它使用单独的something.tpl(注意扩展名是.tpl 而不是.tpl.php)文件,来覆写Drupal的主题函数theme_something().Drupal的可主题化函数的相关文档可参看Drupal API站点: http://api.drupal.org.
每个模板文件都包含一个HTML骨架,里面嵌套了用来输出动态内容的Smarty标签。Smarty标签比php标签更具有可读性。
下面的主题是从PHPTemplate格式转化为Smarty主题引擎格式的:
为了能够使用基于Smarty的Drupal主题,比如Box_grey_smarty, Bluemarine_smarty, Leaf_smarty,等等,你需要首先安装Smarty主题引擎.
现在,你就可以启用,配置,和使用基于Smarty的Drupal主题了。
相关链接:http://drupal.org/node/27566 , Think in Drupal
Web服务器进程应该对themes/engines/smarty目录下面的templates_c子目录,具有读和写的权限.
在linux/unix环境下,解决方案如下:
如果你有chown访问权限的话:
该目录应该由你的用户拥有,一个小组也拥有这个目录的所有权,这个小组中就包括你的web服务器。比如(你的用户名称):apache。
所有者及小组成员对这个目录具有“读/写/执行”的权限,而其它用户具有“读/执行”的权限。
你可以使用<?php phpinfo(); ?>来查看'apache'是不是属于这个用户组的。
chmod 775 templates_c
chown (your user name):apache templates_c
更可能的是,你没有chown访问权:
那么,一个简单的命令:
chmod 777 templates_c
就能为这个目录设置正确的权限了。
如果具有chown访问权的话,就是用前者。最好不要让所有人对这个目录都具有写的权限,但是很多情况下,不得不如此。
在win32下面,你也必须让web服务器进程对templates_c子目录具有写的权限。
在你的Drupal主题目录下,创建一个名为smartytemplate.php的文件.
<?php
function _smarty_variables($hook, $vars = array()) {
switch ($hook) {
case 'page':
if ((arg(0) == 'admin')) {
$vars['template_file'] = 'admin';
}
break;
}
return $vars;
}
?>
参看:
由于Smarty主题引擎是从phptemplate移植过来的,所以可参考phptemplate的解决方案.
在你主题目录下的smartytemplate.php文件(参看: smartytemplate.php:你主题的发动机)中,通过实现一个_smarty_variables函数,就可以简单的添加额外的变量了.
例如
假如你的drupal默认主题为box_grey_smarty:
在themes/box_grey_smarty/目录下,添加一个smartytemplate.php文件,将下面的内容拷贝进去:
<?php
function _smarty_variables($hook, $variables) {
switch($hook) {
case 'comment' :
$variables['newvar'] = 'new variable';
$variables['title'] = 'new title';
break;
}
return $variables;
}
?>
接着,你就可以在你的comment.tpl使用新的变量{$newvar}了.
更多例子可参看phptemplate的模板变量添加,记住将_phptemplate_variables替换为_smarty_variables.
如果需要Drupal Smarty主题系统的高级特性的话,那么你需要在你的主题目录下面创建一个额外的文件(例如, e.g. themes/blumarine_smarty/smartytemplate.php).
这个文件可用来
下面是一个覆写主题函数的例子:
(是从phptemplate中借鉴过来的)
首先你需要找到要覆写的主题函数的所在位置。你可以在Drupal API文档中找到一列这样的例子。在这里我们以theme_item_list()为例。
如果你要覆写的主题函数不包含在基本列表中(block, box, comment, node, page),你需要将它告诉给Smarty主题引擎.
为了实现这一点,你需要在你主题目录下,创建一个t smartytemplate.php文件.这个文件应该包含必须的<?php …?>标签,还有用于主题覆写的存根(stubs),这些存根告诉引擎使用哪个模板文件,以及向其中传递哪些参数.
theme_item_list()大概是这样定义的:
<?php
function theme_item_list($items = array(), $title = NULL, $type = 'ul') {
?>
现在,你需要在你主题的smartytemplate.php文件中加一个存根,如下所示:
<?php
/**
* Catch the theme_item_list function, and redirect through the template api
*/
function smarty_item_list($items = array(), $title = NULL, $type = 'ul') {
// Pass to Smarty Theme Engine, translating the parameters to an associative array. The element names are the names that the variables
// will be assigned within your template.
return _smarty_callback('item_list', array('items' => $items, 'title' => $title, 'type' => $type));
}
?>
我们将函数名中的“theme”替换为了“smarty”,并调用函数_ smarty _callback()以将参数($items和$title)传递给Smarty主题引擎。
另外,在函数名中,除了'smarty'以外,你还可以使用主题的名字(例如,如果主题为bluemarine_smarty的话,那么bluemarine_smarty_item_list 和smarty_item_list都可以使用)。
现在,你可以在你主题目录下,创建一个item_list.tpl模板文件了,它将用来替代原有的theme_item_list()。这个函数,与原有的theme_item_list()相比,应该采用相同的逻辑。
有时,将显示委托给模板文件并不合适。如果直接使用php会更好的话,你在这里可以不用调用_smarty_callback(),而直接返回输出结果。