2012年4月

setuid、 setgid 和 sticky 权限这几个权限以前从来没有了解过,甚至都不知道有这么三种权限在这里,安装 FreeBSD 的时候,看到了,找了些资料,了解了一下下,这个东西不了解不知道,一了解,解了我很多迷题。

先回忆一下下Linux的权限,在Linux里面或者其它的类Unix系统中,权限是管理得很严格的,任何一个用户做任何事情都必须的相关的权限才行,别人的东西不让你看,你根本就会不知道有这么个东西,那么好了,现在就有了下面这几个问题:

  1. 普通用户修改自己的权限,要修改系统的密码数据库,那密码数据库是不是对所有用户开放?如果是的话,那密码数据还安全?如果不是的话,那为什么所有用户又都可以去修改?
  2. 系统的临时目录 /tmp 里面的所有东西,是规谁的?因为任何一个用户都对这个文件目录有写的权限,那也就是说任何用户都可以修改这个目录里面的内容了,那怎么办?我的临时文件别人也可以删除?

先想想我以前所了解的知识,这种问题还真没法儿解决,但是操作系统却已经解决得很好了,为什么?原因就是今天发现的这三种权限类型。这些权限对于一些 UNIX 操作而言很重要, 因为它们能提供一些一般情况下不会授予普通用户的功能。 为了便于理解, 我们首先介绍真实用户 ID (real user ID) 和生效用户 ID (effective user ID)。

真实用户 ID 是拥有或启动进程的用户 UID。 生效 UID 是进程以其身份运行的用户 ID。 举例来说, passwd(1) 工具通常是以发起修改密码的用户身份启动, 也就是说其进程的真实用户 ID 是那个用户的 ID; 但是, 由于需要修改密码数据库, 它会以 root 用户作为生效用户 ID 的身份运行。 这样, 普通的非特权用户就可以修改口令, 而不是看到 “Permission Denied” 错误了。

setuid 权限可以通过在普通权限前面加上一个数字四 (4) 来设置, 如下面的例子所示:

# chmod 4755 suidexample.sh

这样一来, suidexample.sh 的权限应该如下面这样:

-rwsr-xr-x   1 trhodes  trhodes    63 Aug 29 06:36 suidexample.sh

您会注意到, 在原先的属主执行权限的位置变成了 s。 这样, 需要提升特权的可执行文件, 例如 passwd 就可以正常运行了。

可以打开两个终端来观察这一情形。 在其中一个终端里面, 以普通用户身份启动 passwd 进程。 在它等待输入新口令时, 在另一个终端中查看进程表中关于 passwd 命令的信息。

在终端 A 中:

Changing local password for trhodes
Old Password:

在终端 B 中:

# ps aux | grep passwd
trhodes  5232  0.0  0.2  3420  1608   0  R+    2:10AM   0:00.00 grep passwd
root     5211  0.0  0.2  3620  1724   2  I+    2:09AM   0:00.01 passwd

正如前面所说的那样, passwd 是以普通用户的身份启动的, 但其生效 UID 是 root。

与此对应, setgid 权限的作用, 与 setuid 权限类似, 只是当应用程序配合这一设定运行时, 它会被授予拥有文件的那个组的权限。

如果需要在文件上配置 setgid 权限, 可以在权限数值前面增加数字二 (2) 来运行 chmod 命令, 如下面的例子所示:

# chmod 2755 sgidexample.sh

可以用与前面类似的方法来检视新设定的生效情况, 在组权限的地方的 s 表示这一配置已经生效:

-rwxr-sr-x   1 trhodes  trhodes    44 Aug 31 01:49 sgidexample.sh
注意: 在这些例子中, 尽管 shell 脚本也属于可执行文件的一种, 但它们不会以您配置的 EUID 或生效用户 ID 的身份运行。 这是因为 shell 脚本可能无法直接呼叫 setuid(2) 调用。

我们已经讨论了两个特殊权限位 (setuid 和 setgid 权限位), 它们让用户在使用程序时能够用到更高的权限, 有时这会削弱系统的安全性。 除了这两个之外, 还有第三个特殊权限位: sticky bit, 它能够增强安全性。

当在目录上设置了 sticky bit 之后, 其下的文件就只能由文件的所有者删除了。 这个权限设置能够防止用户删除类似 /tmp 这样的公共目录中不属于他们的文件。 要应用这种权限, 可以在权限设置前面加上数字一 (1)。 例如:

# chmod 1777 /tmp

现在, 可以用 ls 命令来查看效果:

# ls -al / | grep tmp
drwxrwxrwt  10 root  wheel         512 Aug 31 01:49 tmp

这里的结尾的 t 表示了 sticky bit 权限。

这是一个简单的基于 ZODB3 的数据存储,定义了一个用户User类,一个应用 App 类,App类用来操作User数据,连接默认使用的 ZEO。你需要至少安装Python 2.3 及以上版本并且安装了 ZODB 3

#!/usr/bin/env python
# encoding: utf-8
"""
user.py
Created by pantao on 2012-04-17.
Copyright (c) 2012 aitine.com. All rights reserved.
"""
import sys, os
import transaction
from persistent import Persistent
from ZEO import ClientStorage
import ZODB
from ZODB.POSException import ConflictError
from BTrees import OOBTree

class User(Persistent):
    """Class for a user
    """
    def __init__(self, login, email, password):
        self.login = login.lower()
        self.email = email.lower()
        self.password = password

class App():
    """"""
    def __init__(self, conn):
        self.conn = conn
        self.root = self.conn.root()

    def get_users(self):
        if not self.root.has_key('users'):
            return None
        return self.root['users'].values()

    def add_user(self, user):
        if not self.root.has_key('users'):
            print('Create new ')
            self.root['users'] = OOBTree.OOBTree()
            transaction.commit()
        users = self.root['users']
        if not users.has_key(user.login):
            users[user.login] = user
            transaction.commit()
        else:
            return None
        return users[user.login]

def main():
    app = App(ZODB.DB(ClientStorage.ClientStorage('/tmp/zeosocket')).open())
    while 1:
        us = app.get_users()
        if us:
            for u in us:
                print('<User {} : {} >'.format(u.login, u.email))
        l,e,p = raw_input('Enter user info:').split(' ')
        u = User(l,e,p)
        app.add_user(u)

if __name__ == '__main__':
    main()

Markdown 介绍

在教程正式开始之前,请先打开 Markdown 测试工具 ,这里面的所有示例都可以直接在该测试工具中测试,或者你也可以将 Markdown 的脚本下载至自己的电脑测试:

Perl 实现 : Markdown 1.0.1

PHP 实现 : PHP Markdown 1.0.1 : PHP Markdown Extra 1.2.5

Python 实现 : Python Markdown 2.1.1 : 要安装最新版本的 Markdown,在Python中还可以使用下列方法安装

sudo easy_install markdown
     sudo pip install markdown

Markdown 是这样两种东西,首先,他是一个格式化语法结构,其次他是一个使用Perl语言编写的用来实现Markdown至HTML转换的工具,它的设计目标就是让写作者尽可能使用容易阅读的语法书写,而不必被繁琐的HTML标签结构所纠缠,就像下面这样就可以创建一个一级标题的HTML结构:

# Markdown 介绍 #

Markdown 基本语法详解

使用 Markdown 一般情况下是没有可视化编辑界面的(Markdown与HTML的不同点在于它的设计初忠就是要写作者使用转Markdown代码(这里称之为代码吧,其实不是,它就是纯文本)),有一些网站在使用的时候,写作界面分为两个区域,预览区域和编辑区域,编辑区域就是写 Markdown,预览区会动态的将编辑区的修改即时的转换为HTML后展示出来以达到可视化编辑 的效果,但是我们这里所讲的,仅仅只是Markdown的语法。

段落、标题以及块引用

要使用 Markdown创建段落应该是最简单的了,只需要某一段文字顶头书写然后前后空行即可,这里不再做过多说明。

标题的创建也十分简单,这里有两种方法,最常用的就是在要作为标题的文字下一行(不空行)添加超一个的= 号或者 - 号,例如:

这是一个一级标题
==============

这是一个二级标题
---------------------------

将被转换成为:

<h1>这是一个一级标题</h1>

<h2>这是一个二级标题</h2>

另外一种创建标题的方法能创建出所有六级标题,它们分别是在要创建标题的那一行方案最前面顶行创建一个或者多个 # 号,并以相同数量的 # 结尾,比如:

### 这是三级标题 ###

##### 这是五级标题

将被转换成为:

<h3>这是三级标题</h3>

<h5>这是五级标题</h5>

你可以看到,后面结尾的 # 是可以省略的。

块级引用的创建是在需要作为块引用的所有文本行前面加上 > 符号,比如:

> 这是块引用的第一行,将会成为块引用中的一个段落
>
> 这是块引用的第二段
> ## 这是块引用中的一个二级标题

转被转换成为下面这样的HTML代码:

<blockquote>
<p>这是块引用的第一行,将会成为块引用中的一个段落</p>

<p>这是块引用的第二段</p>

<h2>这是块引用中的一个二级标题</h2>
</blockquote>

加强与强调

Markdown 使用 星号(*) 与 下划线(_) 表示加强与强调,如下所示:

胡潇说:”我 *不想* 和鸟蛋睡一起!“
刘钊说:“我 _更不想_ 和潇老娘子睡一起!”

然后潘韬就说了:“你们两个都 **不要睡了** 吧!”
潘韬还说了:“ __都跟__ 哥睡!”

将被转换成为:

<p>胡潇说:”我 <em>不想</em> 和鸟蛋睡一起!“
刘钊说:“我 <em>更不想</em> 和潇老娘子睡一起!”</p>

<p>然后潘韬就说了:“你们两个都 <strong>不要睡了</strong> 吧!”
潘韬还说了:“ <strong>都跟</strong> 哥睡!”</p>

列表

无序列表可以使用星号,加号或者减号(*、+、-)创建,它们三者效果都是一样的,比如下的示例:

* 第一项
* 第二项
* 第三项

+ 第一项
+ 第二项
+ 第三项

还有

- 第一项
- 第二项
- 第三项

都会被转换成为:

<ul>
<li>第一项</li>
<li>第二项</li>
<li>第三项</li>
</li>

有序列表则直接在每一项前面加上阿拉伯数字与小数点即可,如下:

1. 有序列表第一项
2. 有序列表第二项
3. 有序列表第三项

将被转换成为:

<ol>
<li>有序列表第一项</li>
<li>有序列表第二项</li>
<li>有序列表第三项</li>
</ol>

如果您在每一个列表项之间添加了一个空行,那么主会在每一个列表项中创建一个段落,比如:

* 这是一个列表项

  这是第一列表项中的第二个段落

* 这是列表的另一个项目

将被转换成为:

<ul>
<li><p>这是一个列表项</p>
<p>这是第一列表项中的第二个段落</p></li>
<li><p>这是列表的另一个项目</p></li>
</ul>

链接

Markdown使用两种方法创建链接,行内创建与引用创建,行内创建一般使用得比较多,它的创建语法是下面这样的:

这是一个示例[链接](http://aitine.com)。

转换成为:

<p>这是一个示例<a href="http://aitine.com">链接</a>。</p>

如果要为链接指定 title 属性,刚可以使用下面这样的:

这是一个示例[链接](http://aitine.com "这是一个标题")。

而以引用的方式创建链接一般用在学术论文上面比较多,或者另一种情况,如果某一个链接在文章中多处使用,那么使用引用 的方式创建链接将非常好,它可以让你对链接进行统一的管理,这有点类似于 LaTeX 对文档引用的管理,创建方法如下:

我最喜欢的几个网站是[Google][1] 、[艾天科技][2] 以及 [自己的博客][3],但是最喜欢的却是 [艾天项目管理平台][atoa],因为 [谷歌][1] 老是不能被访问到。

[1]:http://www.google.com "Google"
[2]:http://aitine.com "Aitine Technology"
[3]:http://note.costony.com "Notes of Cos Tony"
[atoa]:http://us.aitine.com "艾天项目管理平台"

转换后得到:

<p>我最喜欢的几个网站是<a href="http://www.google.com" title="Google">Google</a> 、<a href="http://aitine.com" title="Aitine Technology">艾天科技</a> 以及 <a href="http://note.costony.com" title="Notes of Cos Tony">自己的博客</a>,但是最喜欢的却是 <a href="http://us.aitine.com" title="艾天项目管理平台">艾天项目管理平台</a>,因为 <a href="http://www.google.com" title="Google">谷歌</a> 老是不能被访问到。</p>

从Markdown链接的引用创建方法中,你应该了解到一些基本的引用管理知识,要知道,专业的印刷行业里面、学术论文写作里面一般都不是使用 Word 或者什么可视化编辑工具来书写的,因为这些工具的引用管理功能都太弱了,而且使用起来十分的麻烦,一般都是使用 Tex 或者 LaTex ,有兴趣的可以去了解一下。

图片

图片的语法与链接的十分相似,行内创建时:

![Alt 文本](/path/to/img.jpg "图片 Title")

转换成为:

<p><img src="/path/to/img.jpg" alt="Alt 文本" title="图片 Title" /></p>

引用式创建也很相似:

![Alt 文本][id]

[id]:/path/to/img.jpg "标题"

转换结果是:

<p><img src="/path/to/img.jpg" alt="Alt 文本" title="标题" /></p>

可以看到图片与链接的引用方式创建时,引用格式是一样的,所以,一个引用可以同时被图片和链接所使用。

添加代码段

添加代码段应该是最简单的了,只需要把你想发布的任何代码不要顶头写就行,一般在前面留四个空格,比如:

我们的代码如下:

    <p>这是HTML代码</p>

转换后得到:

<p>我们的代码如下:</p>

<pre><code>&lt;p&gt;这是HTML代码&lt;/p&gt;
</code></pre>

所有以如特殊符号都会被自动转义。

Markdown 语法进阶

在 Markdown 中嵌入原生HTML代码

在 Markdown 代码中嵌入 HTML代码,如果你想直接在 Markdown 中嵌入HTML代码,那么你只需要将代码直接写在需要的地方即可:

这是一个段落。

<table>
    <tr>
        <td>这是用原生的HTML代码写的表格。</td>
    </tr>
</table>

这是另一个段落。

转换之后得到:

<p>这是一个段落。</p>

<table>
    <tr>
        <td>这是用原生的HTML代码写的表格。</td>
    </tr>
</table>

<p>这是另一个段落。</p>

你还需要注意的一点是,Markdown 是不处理HTML的块级元素中的内容的,比如你可以直接在 <p> 标签中使用 * 号:

<p>这是一个 *段落*</p>

在转换之后还是:

<p>这是一个 *段落*</p>

相反的,HTML的行内标签却可以在 Markdown 的语法的任何地方使用(除了代码段中):

这是一个文本段,<span>这个被span标签</span> 包裹了。

转换后得到:

<p>这是一个文本段,<span>这个被span标签</span> 包裹了。</p>

行内元素包括 span*、*cite*、*del 等。

自动转义特殊字符

在HTML中,有两个字符是十分特殊的,他们是 < 和 & 符号,< 表示一个HTML标签的开始,&表示HTML特殊字符转义符的开始,在 Markdown 中这两个字符将会自动被转义,就如你刚才所看到的这一段话:

在HTML中,有两个字符是十分特殊的,他们是 < 和 & 符号,< 表示一个HTML标签的开始,&表示HTML特殊字符转义符的开始,在 Markdown 中这两个字符将会自动被转义,就如你刚才所看到的这一段话:

Markdown 转换之后得到的将是:

<p>在HTML中,有两个字符是十分特殊的,他们是 &lt; 和 &amp; 符号,&lt; 表示一个HTML标签的开始,&amp;表示HTML特殊字符转义符的开始,在 Markdown 中这两个字符将会自动被转义,就如你刚才所看到的这一段话:</p>

注意出现特殊符号的那几个位置。现在如果你想将一段文本指定到下面的这个URL中:

http://aitine.com/aitine?category=1&tag=3

Markdown 自动将您转换成为:

http://aitine.com/aitine?category=1&amp;tag=3

但是并不任何时候出现这些特殊自符的时候 Markdown 都会转义它们,比如前面说的,如果你写的就是完整的HTML代码,那么 Markdown 会不去管它,同样的,如果你写的是完整的HTML转义符,Markdown 也不会去管它,比如:

&copy;

Markdown 将不会去处理它,但是如果是:

AT&T

则会被转义成为:

AT&amp;T

块级元素

段落与分行

一个段落就是前面都有空行的一行或者多行文本(空行的意思是说你看上去像空行的东西,比如一行内没有任何内容或者一个或者多个空格或者制表符都属于空行),任何段落都不能以空格或者制表符开始。

在 Markdown 中,所谓的一行或者多行是指:没有空行分隔的任意行文本,这个和许多其它 文本至HTML转换工具不同,Markdown 使用的是 _硬包裹_,它不会像其它转换工具那样转换成为 <br /> 标签。

如果你是真的想在一个段落中插入一个 <br /> 标签,那么只需要在你想插入的地方输入两个或者两个以上的空格,然后回车,Markdown 就会在这里强制为你换行。

块级元素是可以嵌套的,比如一个 li 里面可能会嵌套的块引用,这时,块引用所使用的 > 符号应该再缩进了,比如下面这样的:

* 这是第一条引用项目

    > 这是第一条引用的内容

* 这是第二个项目

最终会被转义成为:

<ul>
<li><p>这是第一条引用项目</p>
<blockquote>
<p>这是第一条引用的内容</p>
</blockquote></li>
<li><p>这是第二个项目</p></li>
</ul>

在这里我们还需要注意另外一个问题,根据Markdown 对有序列表的定义,所有以数字后面跟着点开头的一行,都将成为一个有序列表,比如:

1234. 这其实并不是一个有序列表项目

也会被转换成为:

<ol>
<li>这其实并不是一个有序列表项目</li>
</ol>

如果有哪里必须这样的形式开头,你可以使用  这个符号来明确的告诉 Markdown 这里是不需要转换的:

1234. 这其实并不是一个有序列表项目

将被转换成为:

<p>1234. 这其实并不是一个有序列表项目</p>

代码块

对于程序员写作,经常需要在文章中嵌入代码段,在Markdown中,要嵌入代码段,只需要将每一行代码前都至少添加四个空格或者一个制表符(如果你想保证代码展示的方式和你想要的展示样式一样,那么最好每一行代码缩进的间距都是一行,我推荐都使用四个空格),Markdown 会将这样的文本都当作需要展示的文本内容(而不是可执行的代码),如果有需要转义的,比如 < 等都会被转义成为转义符,而整个代码段都会被 <pre> 以及 <code> 标签包裹。

这是一个普通的文本段落。

    <code>但是这一行却会被当作是程序代码</code>

上面的文字转换之后得到:

<p>这是一个普通的文本段落。</p>

<pre><code>&lt;code&gt;但是这一行却会被当作是程序代码&lt;/code&gt;
</code></pre>

如果要在行

分割线

你可以使用下面这种方法在内容中插入分割线 <br /> :   * * *

***

*************

- - -

---------------------

上面的任何一行都可以被转换成为:

<hr />

规则就是使用星号或者减号,排成一排,并且数量超过三个,任何两相相邻的符号之间的距离不能超一个空格

PHP Markdown Extra 介绍 (仅限 PHP Markdown Extra 版本)

上面我们已经完整的学习了 Markdown 官方 Perl 版本的基本使用方法,但是 Markdown 不止Perl一种语言的实现,在任何时候都应该尽可能的先考虑 Markdown 的基本实现,如果基本实现不支持的功能时,再考虑其它的实现,而本站所使用的Markdown为PHP的扩展实现,它在官方版本上,还增加了许多实用的功能,下面对其进行详细的介绍:

HTML 块级元素中的 Markdown 语法

在上面的介绍中我们已经说过,Markdown 是不对HTML块级元素中的内容作任何处理的,所以,你写进一个 <div> 标签中的任何内容都不可能享受到 Markdown 带来的方便,但是在 PHP Markdown Extra(以下简称PME)中,你却可以通过一种方式实现,即在其内容也需要提供 Markdown支持的块级元素标签中加入:markdown=“1” 这个属性和值:

<div markdown="1">
这是一段由 *PHP Markdown Extra* 转换的内容
</div>

将成转换成为:

<p>这是一段由 <em>PHP Markdown Extra</em> 转换的内容</p>

标题元素的ID属性

在PME中,你可以对标题设定ID属性,只需要在标题行在后面加下给大括号包裹的CSS ID选择符即可,比如:

这是文章最上面的标题        {#article-header}
==================

## 这是某一个段落的标题 ##    {#some-part}

输出为:

<h1 id="article-header">这是文章最上面的标题</h1>

<h2 id="some-part">这是某一个段落的标题</h2>

然后,创建了带ID属性的标题之后,我们就可在文章的任何地方使用链接指向该标题了:

<p><a href="#some-part">某段的标题</a></p>

强制代码块

在原生的 Markdown 中,代码块必须以每一行增加统一的缩进(一般为四个或者一个制表符)来表示,但是在PME,还提供了一种强制代码段的实现方式,即在任何要作为代码段的文本前后分别加上三个或者超过三个的“*~*” 字符即可,但必须保证前后符号数量是一样的,使用这种方法创建代码段,代码不再需要缩进:

这是一个普通的段落

~~~~~~~~
这个是一行代码
~~~~~~~~

输出为:

<p>这是一个普通的段落</p>

<pre><code>这个是一行代码
</code></pre>

在下面学习定义列表时,你还可以发现,在定义列表的结尾,必须使用这种方式来创建代码段。

表格

PME 有自己专有的语法来创建简单的表格,“简单”的表格如下:

姓名|性别|年龄
-|-|-
潘韬|非女|24
刘钊|鸟蛋|2x
胡潇|娘子|2x

第一行将创建表格的头部,第二行分隔表格的头部与主体部分,从第三行开始,每一行为一个表格行,列与列之间都是通过“*|*”字符分隔,上面的字符输出的的表格如下:

<table>
<thead>
<tr>
  <th>姓名</th>
  <th>性别</th>
  <th>年龄</th>
</tr>
</thead>

<tbody><tr>
  <td>潘韬</td>
  <td>非女</td>
  <td>24</td>
</tr>
<tr>
  <td>刘钊</td>
  <td>鸟蛋</td>
  <td>2x</td>
</tr>
<tr>
  <td>胡潇</td>
  <td>娘子</td>
  <td>2x</td>
</tr>
</tbody></table>

需要注意的是,表格要求一行中至少有一个 “|*” 字符,这使得如果你想创建一个只有一列的表格,那么必须至少在每一行的前面或者后面或者两头都加上 *| 。

PME 还支持为表格中不同的列指定不同的文本对齐方向,默认的都是向左对齐,如果某一列需要向右对于,只需要在其头部与主体分隔行中,那一列的最后面加上冒号“*:*” 即可,如下示例:

产品 价格
.com域名 120元/年
10G VPS 500元/年
网站建设 根据不同的网站需求报价

输出的代码如下:

<table>
<thead>
<tr>
  <th>产品</th>
  <th align="right">价格</th>
</tr>
</thead>

<tbody><tr>
  <td>.com域名</td>
  <td align="right">120元/年</td>
</tr>
<tr>
  <td>10G VPS</td>
  <td align="right">500元/年</td>
</tr>
<tr>
  <td>网站建设</td>
  <td align="right">根据不同的网站需求报价</td>
</tr>

</tbody></table>

在表格中,你同样还可以使用任何行内语法,如下:

| 函数名称 | 说明                    |
| ------------- | ------------------------------ |
| `help()`      | 展示帮助窗口       |
| `destroy()`   | **摧毁你的电脑**     |

定义列表

PME 对定义列表也提供了支持,定义方式如下:

潘韬
:    有点儿像疯子的神经病

刘钊
:    有点儿像神经病的疯子

胡潇
:    是啥?

输出为:

<dl>
<dt>潘韬</dt>
<dd>有点儿像疯子的神经病</dd>

<dt>刘钊</dt>
<dd>有点儿像神经病的疯子</dd>

<dt>胡潇</dt>
<dd>是啥?</dd>
</dl>

定义列表允许你在定义描述时使用多行,第二行开始不再需要缩进,如下所示的定义描述中,后面两行的效果是一样的:

定义标题
:    定义的第一行
     这是第二行
这是第三行

如果在定义描述中需要分段,则只需要在段与段之间增加一个空行,并且保证第二段的第一行与定义描述的第一行使用同样的缩进即可:

定义标题
:    定义的第一行
     这是第二行
这是第三行

     这是第二段
     这是第二段二行
这是第二段第三行

如果一个定义有多个定义描述,那么只需要使用多个“*:*” 即可:

定义标题
:    定义的第一个描述
:    这是第二个描述

:     这是第三个描述

输出为:

<dl>
<dt>定义标题</dt>
<dd>定义的第一个描述</dd>

<dd>这是第二个描述</dd>

<dd>
<p>这是第三个描述</p>
</dd>
</dl>

从上面的示例中我们可以看到,如果在一个定义标题创建多个定义描述时,如果某一个定义描述前增加一个空行,则这个定义描述中会创建一个段落,否则将直接将内容包裹在 <dd> 标签中。在定义描述中,我们同样还可以添加代码段,列表,或者块级引用等内容,如下示例:

定义标题
:    这是一个定义描述

          <?php print("Hello PME"); ?>

     > 块引用
     > > 块引用中的引用

     1. 有序列表
     2. 第二项

输出结果为:

<dl>
<dt>定义标题</dt>
<dd>
<p>这是一个定义描述</p>

<pre><code>  &lt;?php print("Hello PME"); ?&gt;
</code></pre>

<blockquote>
  <p>块引用</p>

  <blockquote>
    <p>块引用中的引用</p>
  </blockquote>
</blockquote>

<ol>
<li>有序列表</li>
<li>第二项</li>
</ol>
</dd>
</dl>

脚注

脚注的工作原理与引用方式的链接十分的像,一个脚注由两个元素构成:一个对脚注的引用将会在转换的过程中转义为一个上标元素,并且链接至脚注的定义;一个脚注的将会和其它脚本等放在文档的最末位(位置非必需),如下示例:

生前何必贪睡?死后自会长眠![^1]

[^1]:引用自潘韬撰写的《睡眠经》第一章第一节

输出为:

<p>生前何必贪睡?死后自会长眠!<sup id="1"><a href="#fn:1" rel="footnote">1</a></sup></p>

<div>

<ol>

<li id="1">
<p>引用自潘韬撰写的《睡眠经》第一章第一节&nbsp;<a href="#fnref:1" rev="footnote">↩</a></p>
</li>

</ol>
</div>

Plone 主题可以通过 ZMI 直接在后台进行修改,但是这也只能满足一部分需求,如果我们需要更多可定制的操作,那么就需要完全的创建新的主题,在 Plone 中要创建一个主题,很简单。

使用 paster 创建 Plone 主题

Plone 安装完成之后,在 {instance_path}/bin 目录中,就有 paster,通过它,我们可以创建许多东西,比如创建新的 content type,在这里,我们就使用它来创建新的 Plone 主题。

首先,进入 {instance_path}/src/ 目录:

cd /path/to/instance/src

之后运行下列命令:

../bin/paster create -t plone3_theme aitinetheme.aitine

然后脚本会询问你一些问题,你只需要按着你的实际情况以脚本提供的格式回复即可。脚本运行完成之后,就会在 ./src 目录中生成 aitinetheme.titine 目录,这就是新的 plone 主题目录,之后的所有操作都将在该目录中进行。

使用 sc.paster.theme 创建 plone 主题

本文重点将介绍使用 sc.paster.theme 这个工具来创建 plone 主题,虽然和 paster 有点像,但是我个人还是更喜欢这个工具。

安装 sc.paster.theme

该工具并没有默认提供,我们需要先安装它,使用 easy_install 命令即可,在这里,我们有一点需要记住,如果你的 Zope 服务器使用的是自带的 Python ,那么,你不能直接执行 easy_install ,因为这样会将其安装到全局的 Python 环境中,我们需要指出 Zope 服务器所使用的 Python 路径,比如:

/Applications/Plone/Python-2.6/bin/easy_install -U sc.paster.theme

安装需要 gcc 等工具,你需要先安装,如果你在 Mac OS X 下安装,那么需要安装 XCode 命令行工具集才行,如果你是在 Windows 下面安装,对不起,哥真不知道应该怎么整。

在 {instance_path}/bin 目录中创建 sc.paster.theme 脚本的快捷方式

sc.paster.theme 安装完成之后,进入 {instance_path}/bin 目录,创建 sc.paster.theme 的链接文件,我这里使用的是硬链接,当然一定要记住,你需要为其重新命名,因为 Plone 本身已经有了一个 paster 脚本了。

ln /Applications/Plone/Python-2.6/bin/paster ./sc_paster

使用 sc.paster.theme 创建 Plone 主题

与 paster 一样,先进入 src 目录,然后执行下面这行命令:

../bin/sc_paster create -t portal_theme aitine.theme.aitine

该命令同样会询问你一些问题,你一样需要一个一个回答,如果按我创建我的 aitine.theme.aitine这个主题来讲的话,整个命令的运行流程如下:

PanTao-Macbook-Pro:src pantao$ ../bin/sc_paster create -t portal_theme aitine.theme.aitine
Selected and implied templates:
sc.paster.theme#portal_theme Tema visual para projetos Plone 3
Variables:
egg: aitine.theme.aitine
package: aitinethemeaitine
project: aitine.theme.aitine
Expert Mode? (What question mode would you like? (easy/expert/all)?) ['easy']: all
Namespace Package Name (Namespace principal) ['aitine']:
Namespace 2 Package Name (Usualmente o nome do cliente) ['theme']:
Package Name (Nome do projeto) ['aitine']:
Description (One-line description of the project) ['']: A plone theme designed by aitine.com, for Aitine.
Creating template portal_theme
Creating directory ./aitine.theme.aitine
Recursing into +namespace_package+
Creating ./aitine.theme.aitine/aitine/
Recursing into +namespace_package2+
......
Copying LICENSE.GPL to ./aitine.theme.aitine/docs/LICENSE.GPL
Copying LICENSE.txt_tmpl to ./aitine.theme.aitine/docs/LICENSE.txt
Copying setup.cfg to ./aitine.theme.aitine/setup.cfg
Copying setup.py_tmpl to ./aitine.theme.aitine/setup.py
Running /Applications/Plone/Python-2.6/bin/python setup.py egg_info

中间很长一段信息已经被我省略了,之后,我们在 src 目录中,就会得到一个名为:@aitine.theme.aitine@ 的文件夹,它就是我们的主题。之后我们还需要将其添加到 buildout.cfg 文件中,添加方式如下:

eggs =
......
aitine.theme.aitine
zcml =
......
aitine.theme.aitine
develop =
......
src/aitine.theme.aitine

添加完成之后,我们需要更新我们的 instance,然后再以 fg 模工运行 Plone:

PanTao-Macbook-Pro:zinstance pantao$ ./bin/buildout
Develop: '/Applications/Plone/zinstance/src/aitine.theme.aitine'
Uninstalling zopeskel.
......
PanTao-Macbook-Pro:zinstance pantao$ ./bin/plonectl fg
instance: 2012-03-19 20:47:44 INFO ZServer HTTP server started at Mon Mar 19 20:47:44 2012
    Hostname: 0.0.0.0
    Port: 8080
2012-03-19 20:47:45 WARNING SecurityInfo Conflicting security declarations for "setText"
2012-03-19 20:47:45 WARNING SecurityInfo Class "ATTopic" had conflicting security declarations
2012-03-19 20:48:01 INFO Zope Ready to handle requests

当你看到 Zope Ready to handle requests 信息时,你的 Plone 已经以 fg 模式运行成功了,现在你需要进入你的开发站点,启用这个主题即可,以 fg 模式运行时,你对主题的修改能即时的展示在前端。

确实是快要疯了,昨天为 Aitine 重新写了主题,基于 Diazo 的,但是在服务器上面怎么也无法安装,最让人头疼的是,在本地各种环境下都能使用,唯一不能使用的就是在服务器上面,这让人很恼火,昨天一个晚上也没有找哪里出错,因为是ZEO安装吧,Buildout时也不报错,使用启动脚本启动也不报错,但是就是启动不了。

今天找了一个以前在老服务器上可用的Diazo Plone Theme,发现也运行不了,这让我确定,并不是我的主题有问题,然后想到看看是不是服务器环境的问题,因为我就这两天把服务升级到了Ubuntu 64位服务器系统,接着我就把刚刚停用的32位系统启动,把主题在32位系统上面安装,没有什么问题,最终确定了:服务器环境的问题。

知道问题出在服务器上面,就好办事儿了,Diazo主题必须要有LXML支持,在其它环境都一致的情况下,只有可能是这个地方出问题,所以我就把 lxml 的 egg 删除之:

root@aitine:/usr/local/plone/buildout-cache/eggs# rm -rf lxml-2.2.8-py2.6-linux-x86_64.egg/

之后 还需要对 buildout.cfg 文件作如下修改:

[buildout]
parts =
lxml # 保证 lxml 一直在第一位
...

之后重新 Buildout 出来,但是又出问题了,无法 buildout,报下面这个错误:

...
...
An error occured when trying to install lxml 2.2.8. Look above this message for any errors that were output by easy_install.
While:
Installing instance.
Getting distribution for 'lxml==2.2.8'.
Error: Couldn't install: lxml 2.2.8

这个在 这里 找到解决方法:

sudo apt-get install libxml2-dev libxslt-dev

这个安装完成之后,再重新 buildout 即可。

Plone主题默认提供了几个Portlet是无法在管理页面中去除的,但是我们的Diazo主题中可以加一个 portlets.xml 配置文件,然后使用下面的代码即可去除之:

<?xml version="1.0"?>
<portlets>
<assignment remove="true" name="login" category="context" key="/" manager="plone.leftcolumn" type="portlets.Login" />
<assignment remove="true" name="navigation" category="context" key="/" manager="plone.leftcolumn" type="portlets.Navigation" />
<assignment remove="true" name="calendar" category="context" key="/" manager="plone.rightcolumn" type="portlets.Calendar" />
<assignment remove="true" name="events" category="context" key="/" manager="plone.rightcolumn" type="portlets.Events" />
<assignment remove="true" name="news" category="context" key="/" manager="plone.rightcolumn" type="portlets.News" />
<assignment remove="true" name="recent-items" category="context" key="/" manager="plone.rightcolumn" type="portlets.recent" />
<assignment remove="true" name="review" category="context" key="/" manager="plone.rightcolumn" type="portlets.review" />
</portlets>