创建 OpenACS 软件包

作者:Reuven M. Lerner

上个月,我们继续探索 OpenACS,了解了如何使用 APM(ArsDigita 软件包管理器)将各个应用程序打包在一起。每个 OpenACS 应用程序通常将数据库表与服务器端程序结合在一起,这意味着安装或升级软件包比复制一些文件要复杂得多。正如我们上个月所见,APM 应用程序使得安装软件包,然后在一个或多个 URL 下实例化它相对容易。

如果您只关心使用现有的 APM,这当然很好。但是大多数 OpenACS 安装都需要新的、自定义的软件包,这些软件包有自己的数据模型和程序。虽然理论上可以在没有 APM 的情况下开发 OpenACS 应用程序,但这样做会使分发您的软件、跟踪版本或标准化您的安装过程变得困难。

本月,我们将介绍如何使用 APM 开发我们自己的简单 Web/数据库应用程序。最终结果是一个其他人可以加载到他们的 OpenACS 系统中的应用程序。

创建骨架软件包

创建新 OpenACS 应用程序的第一步是通过基于 Web 的 APM 程序创建一个新的骨架软件包。默认情况下,此程序仅对具有 OpenACS 系统管理权限的用户可用,因此您可能需要在开始开发工作时请求管理员修改权限。

在大多数 OpenACS 系统上,您可以通过 /acs-admin/apm/ URL 启动 APM 程序。此程序显示所有已安装的 APM,包括每个 APM 的名称、版本和包含的文件数。文件计数通常包括 .sql 文件(用于创建和删除数据库定义)、.tcl 文件(包含 Tcl 程序代码)、.adp 文件(用于类似于 ASP 或 JSP 的 Web 模板)和 .xql 文件(包含 SQL 查询)。但是,APM 可以包含其他文件,包括图像、文本文件,甚至更不寻常的东西,例如 Flash。

正如我们上个月所见,我们可以使用此屏幕作为入口点来安装、检查和修改系统上的软件包。但是,如果我们在列表底部向下滚动并单击“创建新软件包”链接,我们就可以开始创建我们自己的应用程序的过程。

最初的“创建软件包”屏幕要求提供许多参数,这些参数将帮助 APM 创建一个 .info 文件,该文件向系统描述软件包。假设我们想要创建一个最少工作量的“hello, world”应用程序,我们可以填写少量字段

  • 软件包密钥应为 atf-hello。APM 没有命名空间层次结构,但大多数开发人员将他们的姓名(或类似的标识术语)放在软件包名称的前面,以避免冲突。

  • 软件包名称可以是 Hello 或您选择的任何其他名称;开发人员使用它来与其他软件包区分开来。

  • 我们正在开发一个应用程序,而不是服务。

  • 我们的版本号为 0.1d,这意味着它处于开发的早期阶段。

  • 您可以酌情填写或忽略摘要和描述。但是,对于实际应用程序,最好填写这些信息。

完成后,确保选中“写入软件包”指定符框,然后单击页面底部的“创建软件包”按钮。您将被带到此特定软件包的管理屏幕。如果您查看安装 OpenACS 工具包的文件系统上的 packages 子目录,您将看到一个 atf-hello 目录,其中包含一个格式正确的 XML atf-hello.info 文件。

创建数据模型

现在 OpenACS 识别了我们的软件包,我们可以开始设计我们软件包的数据模型了。正如所有经验丰富的 Web/数据库开发人员所知,设计表是最困难的部分;一旦您知道它们将如何工作,创建添加、删除和修改数据的应用程序就非常容易了。

我们的应用程序将为我们提供一个简单的留言簿,网站访问者可以在我们的页面上输入评论。(OpenACS 自带一个更通用和强大的工具,可以在网站上的任何页面上执行此操作,但为了学习如何创建软件包,我们将忽略这一点。)我们的数据模型将如下所示

CREATE TABLE atf_hello_postings (
  posting_id    SERIAL   NOT NULL,
  user_id       INTEGER  NOT NULL  REFERENCES users
                ON DELETE CASCADE
                ON UPDATE CASCADE,
  entry_date    TIMESTAMP  NOT NULL  DEFAULT NOW(),
  posting       TEXT     NOT NULL
                CHECK (posting <> ''),
  PRIMARY KEY(posting_id)
);

从纯技术的角度来看,对于任何使用过 PostgreSQL 的人来说,以前的表定义可能看起来是合理的。我们将 posting_id 设置为整数主键,user_id 设置为外键,一个时间戳字段(包含日期和时间信息),然后是一个文本字段来包含实际的帖子。请注意我们的表名是如何以 atf_hello 开头的,这反映了它在 atf-hello APM 中。保持表名与软件包名称一致是一种原始形式的命名空间管理,但如果每个人都坚持使用它,它就足够好了。

以上在 PostgreSQL 下有效,但不保证在 Oracle 下有效。鉴于 OpenACS 社区以透明地与这两个数据库协同工作而自豪,我们应该怎么做才能避免疏远我们的高端同事呢?

答案是,在安装 atf-hello 软件包时,APM 会查找名为 sql/atf-hello-create.sql 的文件。如果存在这样的文件,则假定它适用于所有支持的数据库。否则,APM 会查找名为 postgresql 和 oracle 的子目录,并在相应的目录中执行 atf-hello-create.sql。因此,如果您的系统使用 PostgreSQL,则上面的 SQL 将保存在名为 sql/postgresql/atf-hello-create.sql 的文件中。官方的 OpenACS 软件包应该开箱即用地与 Oracle 和 PostgreSQL 配合使用,这意味着很少找到仅适用于一个品牌或其他品牌的软件包。(本月专栏中的示例保证仅适用于 PostgreSQL,尽管将它们移植到 Oracle 应该不会太困难。)

OpenACS 还允许我们创建一个清理脚本,名为 sql/PACKAGE-drop.sql,它会删除 create 脚本定义的所有表和存储过程。因此,我们创建了一个名为 sql/postgresql/atf-hello-drop.sql 的文件。

APM 知道如何在首次安装软件包时创建数据模型,但在您进行开发工作时则不知道。因此,要将 atf-hello 安装到数据库中,您需要手动处理它

psql -f atf-hello-create.sql openacs4

当然,这假设 openacs4 是您的 OpenACS 数据库的名称,PostgreSQL 服务器与 psql 客户端在同一台机器上运行,并且您当前的用户名称具有对该数据库的访问权限。

OpenACS 模板

现在我们的数据模型已经安装好了,是时候编写一个使用它的应用程序了。OpenACS 4 引入了一个新的模板系统,该系统构建在 ADP(AOLserver 等效于 ASP 或 JSP)之上,我发现它是 OpenACS 最好的部分之一。

对于非程序员来说,类似于 ASP 的页面比标准的服务器端程序更容易理解。但是,HTML 和程序的混合往往会成为瓶颈,因为设计人员和开发人员无法同时处理该文件。

OpenACS 模板是解决此问题的一个令人耳目一新的解决方案。我们将页面分为两个部分:一部分(.tcl 页面)供程序员使用,另一部分(.adp 页面)供设计人员使用。.tcl 页面以一个契约开始,描述了它期望接收哪些值,以及它将向 ADP 页面提供哪些值。Tcl 页面以调用 ad_return_template 结束,它会查找同名的 .adp 页面,适当地替换变量,然后在该页面上运行 ADP 解析器。

Tcl 页面可以以数据源的形式将值传递给 ADP 页面,数据源是变量的别称。如果 Tcl 页面说

set five 5

ADP 页面可以在任何地方通过将变量名用 @ 符号括起来来检索此值,如 @five@。如果变量 five 没有在页面契约中定义或导出,OpenACS 会产生运行时错误,抱怨不存在这样的变量。

这种职责分离意味着设计人员和程序员可以独立工作,只要页面契约中描述的约定的接口(对于 Tcl 页面的输入和输出)保持不变即可。

创建 Tcl 页面

OpenACS 附带了大量旨在帮助程序员快速轻松地创建 HTML 表单的函数。为了便于介绍,我们不会使用这些函数;我们将使用简单的原始 HTML。

我们的应用程序将包含两个 URL

  • 一个 URL 将按时间顺序显示 atf_hello_postings 表中的当前条目,以及一个用于输入新帖子的表单。我们将使用 OpenACS 数据库 API,它比原生 AOLserver 数据库 API 更易于使用(并且使我们不必担心线程和数据库池),从数据库中检索结果并将它们放入一个多行变量中。然后,此变量将作为数据源传递给 ADP 页面,并在其中显示。此表单的操作将指向第二个 URL。

  • 另一个是接收 HTML 表单、在数据库中输入新行并将人们重定向回第一个页面的 Tcl 程序。

换句话说,我们的应用程序将需要两个 Tcl 页面和一个 ADP 页面。这些都将放在 atf-hello 下的 www 子目录中;如果该目录不存在,请创建它,并仔细检查它是否与 AOLserver 运行的用户属于同一用户。

第一个 Tcl 页面 (posting.tcl)(如清单 1 所示)不期望任何参数,并导出一个名为 postings 的数据源。Tcl 页面以调用 ad_page_contract 开始,它允许我们注释文件的所有者和用途(在第一个参数中)、我们收到的输入(在第二个参数中,空白 posting.tcl)以及我们导出的数据源(在第三个参数中,由于历史原因命名为属性)。Tcl 页面以调用 ad_return_template 结束,它会查找与当前 .tcl 文件同名的 .adp 文件。

清单 1. posting.tcl

虽然每个数据源实际上都是一个简单的 Tcl 变量,但 OpenACS 模板系统在某种程度上掩盖了这些变量的真实性质,为每个数据源分配一个名称和一个类型(multirow、list、onevalue 或 onerow)。在这种特定情况下,我们的 postings 数据源是一个 multirow,这意味着它将包含来自我们的 SELECT 的多个结果行。db_multirow 过程接受三个参数:应将行读取到的变量的名称、查询的名称和 SQL 本身。

命名查询在 OpenACS 中既是福音也是诅咒。它们是福音,因为它们可以通过将查询放在外部 .xql 文件(适用于特定数据库的 XML 格式文件)中来与多个数据库协同工作。问题在于,OpenACS 首先查找与查询名称关联的 .xql 文件,如果不存在 XML,则仅查看 .tcl 页面中的 SQL。许多 OpenACS 初学者惊讶地发现系统忽略了他们在 .tcl 页面中的 SQL 修改,而是查看了 .xql 页面。

创建 ADP 页面

当 posting.tcl 通过调用 ad_return_template 结束时,OpenACS 模板系统会查找 posting.adp。由于我们的 Tcl 页面包含所有的 Tcl 程序代码,因此我们不需要 ADP 页面中的标准 <% %> 标记。但是,我们确实需要一种将我们的数据源转换为 HTML 中可用的内容的方法。

清单 2. posting.adp

如清单 2 (posting.adp) 所示,OpenACS 模板系统添加了一些标记,使得可以以直接的方式检索在 .tcl 页面中设置的值

  • 我们可以使用 <if> 标记在输出页面中有条件地包含 HTML,该标记比较(使用 eq 和 ne)两个值。在 posting.adp 中,我们将 postings 数据源中的行数(通过查询 @postings:rowcount@)与 0 进行比较。如果没有要显示的行,那么我们什么也不显示。

  • 如果有要显示的行,我们使用 <multiple> 标记遍历它们。在 <multiple> 标记块中,我们可以使用 @NAME.column@ 语法检索单个数据库列,如您在 posting.adp 中看到的那样。

无论我们显示多少行,posting.adp 始终包含一个小的 HTML 表单,该表单将其内容发送到 posting-add 程序。此程序 posting-add.tcl(如清单 3 所示)以 ad_page_contract 开头,该契约声明一个输入变量 (posting_text)。默认情况下,输入变量是强制性的,尽管我们可以通过修改 ad_page_contract 中的条目将它们声明为可选或分配默认值。在这种特定情况下,我们要求 OpenACS 删除我们收到的文本输入中的任何前导或尾随空格。

清单 3. posting-add.tcl

然后,我们使用内置的 ad_get_user_id 过程检索当前用户 ID,将其分配给 user_id 变量。接下来,我们使用 db_dml 将帖子插入数据库。请注意,我们在 db_dml 中在变量名称之前使用冒号(而不是美元符号);这是 OpenACS 数据库 API 中的标准做法,并确保我们在将数据传递到数据库服务器时不会遇到引号问题。

最后,posting-add.tcl 通过将用户重定向到 posting 来结束,这将调用 posting.tcl 并显示 posting.adp。

完成软件包

我们现在可以返回 APM 并使用我们的模板和数据库创建脚本生成一个软件包。单击 atf-hello 软件包名称,然后单击页面底部的“管理文件信息”链接。现在单击“扫描此软件包中的其他文件”。您应该看到我们安装的 .sql、.tcl 和 .adp 页面的列表。指示所有这些文件都应包含在软件包中,并在返回主 ATF Hello APM 管理屏幕后,单击“生成新的 atf-hello.info 文件”链接。

您现在可以创建一个可以分发给任何其他 OpenACS 用户的 APM 了。单击“生成文件”链接,分发文件信息将指示生成的 APM 的大小。如果您单击此链接,则应将 APM 下载到您的系统。

如何安装某人发送给您的新 APM?最简单的方法是将 APM 放在服务器文件系统上。然后从您的 Web 浏览器中,返回主 APM 页面 (/acs-admin/apm/) 并单击“安装”链接。告诉系统 APM 所在的位置,它将被放置在 packages 目录下。然后,您将能够使用我们上个月检查过的 APM 安装程序来安装它。数据模型将被插入到数据库中,并且 Web 页面将可供任何感兴趣的各方使用。当然,一旦软件包安装到系统中,您就可以使用 ACS 站点地图应用程序在您选择的 URL 下挂载软件包的新实例。

我们遗漏了什么?

此示例软件包仅触及了 OpenACS 应用程序开发的表面,例如

  • 模板系统带有一个自动表单构建器系统,可以轻松创建 HTML 表单,这些表单自动提供确认屏幕和数据验证。

  • 我们可以通过在软件包的 tcl 目录中定义 Tcl 过程,在启动时将它们加载到 AOLserver 中。

  • 如上所述,命名 SQL 查询使得编写单个 Tcl 程序成为可能,该程序可以透明地访问 Oracle 和 PostgreSQL。

  • 可以使用 OpenACS 上下文概念将软件包的每个实例与其对等实例分开。

  • 每个实例都可以设置自己的参数,使其具有特定于安装的信息。

  • 每个软件包都可以定义(或使用)自己的一组权限,允许您为系统上的用户和组创建自定义权限和自定义访问控制列表。

结论

OpenACS 很复杂,APM 不是最容易学习的系统,因为它试图处理 Web/数据库开发人员经常遇到的许多复杂情况。同时,我还没有看到一种更简单的方法来分发 Web/数据库应用程序,使其具有如此高的模块化、跨数据库的可移植性以及模板的灵活性。创建此类应用程序的简易性,加上丰富的数据模型和大量已建立的应用程序,使 OpenACS 成为在线社区可行且有用的平台。

电子邮件:reuven@lerner.co.il

Reuven M. Lerner 是一位专门从事 Web/数据库应用程序和开源软件的顾问。他的著作《Core Perl》于 2002 年 1 月由 Prentice Hall 出版。Reuven 与他的妻子和女儿住在以色列的莫迪因。

加载 Disqus 评论