卷2:第13章 Moodle

2018-02-24 15:55 更新

作者:Tim Hunt
译者:Li Shijian(李诗剑)

Moodle是一款为教育系统设计的Web应用。我会对Moodle各个部分如何运作做一个综述,同时我将专注于介绍几个我认为特别有趣的设计:

  1. 用插件分割应用的方法;
  2. 权限系统 —— 它控制着什么用户可以在系统不同的地方做什么事情;
  3. 产生输出的方式 —— 它使得应用不同的主题来更改外观,并且把界面接口分离出来;
  4. 数据库抽象层。

什么是Moodle?

Moddle 提供了一个师生之间的在线教学平台。一个Moodle站点被划分为不同的课程。特定的用户可以以不同的身份参与到一门课程中去,比如学生或者老师。每一门课程都由一系列的资源和活动组成。一个资源可以是一份PDF文档,Moodle的一个HTML页面,或者干脆是一个指向网络上其他位置的链接。一个活动可能是一个论坛,一次测验,或者是一个Wiki。在一门课程中,这些资源和活动以某种方式被组织起来。例如,它们可以按照逻辑上的话题,或者是日程上特定的周目被分配到一起。
图13.2: 大学系统典型架构

  • 一个管理跨系统的用户帐号身份认证服务(例如使用LDAP)。
  • 一个学生信息系统。它就是一个庞大的数据库,里面记载着所有的学生信息,包括他们当前正在进行的课程,以及以后需要完成的课程;还有他们的笔记,这份笔记可以是他们对一门所完成课程的高度总结。当然,这个信息系统也可以提供其他的管理功能,比如跟踪一个学生是否上缴了学费。
  • 一个文档库(比如使用 Alfresco)。它用来存储文件,以及跟踪用户合作维护文件时的工作流。
  • 一个电子档案袋(ePortfolio)。 学生可以在这里存放他们自己的资料(assets)并且把它们组合起来形成其他文档。例如利用这些资料编写一篇CV(简历),或者用来证明档案所有者已经满足了一门实践课的选修条件。

Moodle专注于为所有参与到教学中的人提供一个在线平台,而不是为某一个教育组织特别设计的某个系统。Moodle仅仅为非主要功能提供了最基本的实现,所以它可以单独的作为一个应用,或者与其它系统进行集成。Moodle扮演的角色被正式地称为虚拟教学环境(VLE),或者是教学/课程管理系统(LMS,CMS,甚至LCMS)。

Moodle是一个用PHP编写的开源免费软件(GPL)。它可以在绝大多数的Web服务器和平台上运行。它需要一个数据库,目前支持MySQL,PostgreSQL,MS SQL Server以及Oracle。

Moodle从哪里来?

Moodle项目由Martin Dougiamas在1999年开创,当时他正在澳大利亚的科廷大学工作。1.0版本于2002年发布,当时使用的语言和数据库版本是PHP 4.2和MySQL 3.23。那时的版本从一开始就限制了Moodle可能采用的框架。然而从那以后,整个软件发生了翻天覆地的变化。现在发布的版本是Moodle 2.2.x系列。

13.1. Moodle运作方式综述

安装Moodle的三个部分

Moodle安装由三部分组成:

  1. 代码,通常在一个类似 /var/www/moodle 或者 ~/htdocs/moodle 的目录里。Web服务器应该对这个目录具有写权限。
  2. 数据库,由上面提到过的几种RDMS(RDBMS,关系性数据库管理系统)管理。实际上,Moodle给所有的表名增加了一个前缀。所以如果需要的话,它可以和其他应用共用一个数据库。
  3. moodledata 目录。这个目录用于存储用户上传的文件以及系统生成的文件,同样Web服务器需要有对这个目录的写权限。出于安全考虑,这个目录应该设置于Web根目录之外。

以上三部分可以完全部署在一台服务器上。或者,采用负载均衡的设置,在每台Web服务器上都部署代码,但是仅仅共用一个数据库和一个很有可能在其他服务器上的moodledata目录。

当Moodle安装完毕后,这三个部分的配置信息被存储在moodle根目录下的config.php文件中。

请求调度

Moodle是一个Web应用,所以用户通过浏览器来与之交互。从Moodle自己的视角来看,这就意味着它要响应HTTP请求。Moodle的一个重要设计考量就是URL的名字空间,以及URL如何被调度到不同的脚本上。

Moodle在这里采用PHP标准方法。浏览一个课程的主页时,URL可能像 .../course/view.php?id=123,这里123就是这门课程在数据库中的唯一标识。浏览一个论坛讨论时,URL可能是.../mod/forum/discuss.php?id=456789。也就是说,这些特定的脚本,course/view.php 或者mod/forum/discuss.php 会来处理这些请求。

这对于开发者来说是非常简单的。想要理解Moodle是怎么处理一个特定的请求,你只需要观察URL,从阅读那份php文件的代码开始。但是从用户的角度来看这是十分丑陋的,因为这些URL是永久不变的。比方说一个课程改了名字,或者一个管理员把一个讨论转移到另一个论坛中,这些URL都不会变。(这对于URL来说是一个非常好的性质,正如Tim Berners-Lee在他的文章Cool URIs don't change中提到的)

另一种可以采用的方法是建立一个唯一入口 .../index.php/[其他使请求唯一确定的信息]。这个单独的index.php脚本会通过某种方式将请求进行调度。这个方法添加了一个大多数软件开发者都喜欢用的间接层。缺少了这个间接层并不会影响到Moodle的使用。

插件

和许多其它成功的开源项目一样,Moodle由许多和系统内核协同工作的插件构建起来。这是一个绝妙的主意,因为它可以使用户按照他们定制的方法来增强Moodle的功能。一个开源系统的重要优势在于,你可以根据自己的特定需求来更改它。然而,为代码增加高可定制性的同时,会在系统升级的时候引入大麻烦,即使我们已经采用了很好的版本控制系统。Moodle的插件通过定义好的API与内核交互,所以在自包含的插件中,可以允许尽可能多的用户定制与新特性被开发出来。这也方便了用户根据需求定制自己的Moodle,分享这些定制内容,同时也便于对Moodle系统内核进行升级。

有许多不同的方法可以将一个系统构建成插件化的。Moodle具有一个相对庞大的内核,并且插件是强类型的。我所说的相对庞大的内核,指的是内核提供了大量的功能。这违反了那类,由一个小型的插件启动器进行引导,其余部分都是插件的架构设计。

当我提及插件是强类型的时候,我指的是根据你想要实现的具体功能,你可能需要写完全不同的插件,实现不同的API。比如,一个新的活动模块插件会与一个新的认证插件,或者是提问插件截然不同。根据最后统计,现在我们一共有35种不同的插件(这里有一个Moodle插件类型完全列表)。这违背了那类,所有插件通过使用最基本的API,通过注册它们感兴趣的钩子和事件与内核进行交互的架构设计。

通常来说,Moodle现在有尝试把更多的功能移到插件中以减小内核的趋势。可是这并没有带来巨大的成功,因为当前一个逐渐增长的特性集趋于去扩展内核。另一个趋势是尽可能将不同种类的插件进行规范化。这样在许多公共功能上,比如安装和升级,所有类型的插件都能够按照统一的方式运行。

一个Moodle中的插件其实就是一个包含许多文件的目录。每一个插件都有一个类型和名字,这两个构成了这个插件的"Frankenstyle"组件名称。("Frankenstyle"这个单词出自于开发者Jabber频道的一次讨论,人人都爱它,所以这个单词就被固定下来了)插件的类型和名字决定了这个插件目录的路径。插件类型给定一个前缀,目录名称就是这个插件的名字。这里有一些例子:

| 插件类型 | 插件名称 | Frankenstyle | 目录 |
| mod (Activity module) | forum | mod_forum | mod/forum |
| mod (Activity module) | quiz | mod_quiz | mod/quiz |
| block (Side-block) | navigation | block_navigation | blocks/navigation |
| qtype (Question type) | shortanswer | qtype_shortanswer | question/type/shortanswer |
| quiz (Quiz report) | statistics | quiz_statistics | mod/quiz/report/statistics |

最后的一个例子表明了每一个活动模块被允许声明子插件类型。只有活动模块才能做到这个,出于两点原因。首先如果所有的插件都可以声明子插件类型,这或许会带来严重的性能问题。另外活动模块是Moodle中最重要的教育活动,也是插件中最重要的类型,所以它们应该具有特殊的权限。

示例插件

我会以一个具体的插件实例来解释Moodle架构中的大量细节。作为惯例,我选择实现一个显示"Hello world"的插件。

这个插件实际上并不适合任何一种Moodle标准插件。它只是一个简单的脚本,和其他任何东西都没有联系,所以我选择把它制作成一个'local'类型的插件。这是一个catch-all的插件类型,专门处理一些杂乱的功能,所以在这里再适合不过了。我给我的插件命名为greet,所以它的Frankenstyle的名字是local_greet,路径为local/greet。(插件代码下载)

每一个插件都必需包含一个叫做version.php的文件,这个文件定义了关于这个插件本身的元数据。Moodle的插件安装系统会使用它来对插件进行安装和升级。例如local/greet/version.php包含代码:

<?php
$plugin->component    = 'local_greet';
$plugin->version      = 2011102900;
$plugin->requires     = 2011102700;
$plugin->maturity     = MATURITY_STABLE;    

因为可以从路径上显然地推导出插件的名字,所以乍看之下代码里面包含组件名(component name)略显多余。而实际上,安装器需要通过组件名来验证插件是否安装在正确的位置上。版本(Version)字段定义了这个插件的版本,成熟度(Maturity)是诸如ALPHA,BETA,RC(发布候选版, release candidate), 或者STABLE这样的标签。Requires字段标识着能和这个版本兼容的Moodle最低版本号。必要的话,你也要记录下这个插件依赖的其他插件。

这里是这个简单插件的主要脚本(存储在local/greet/index.php):

<?php
require_once(dirname(__FILE__) . '/../../config.php');        // 1

require_login();                                              // 2
$context = context_system::instance();                        // 3
require_capability('local/greet:begreeted', $context);        // 4

$name = optional_param('name', '', PARAM_TEXT);               // 5
if (!$name) {
    $name = fullname($USER);                                  // 6
}

add_to_log(SITEID, 'local_greet', 'begreeted',
    'local/greet/index.php?name=' . urlencode($name));        // 7

$PAGE->set_context($context);                                 // 8
$PAGE->set_url(new moodle_url('/local/greet/index.php'),
    array('name' => $name));                                  // 9
$PAGE->set_title(get_string('welcome', 'local_greet'));       // 10

echo $OUTPUT->header();                                       // 11
echo $OUTPUT->box(get_string('greet', 'local_greet',
    format_string($name)));                                   // 12
echo $OUTPUT->footer();                                       // 13

Line 1:引导Moodle

require_once(dirname(__FILE__) . '/../../config.php');        // 1

这单独的一行是大多数工作都要首先完成的。我之前说过,config.php包含着Moodle如何连接数据库以及找到metadata目录的细节。然后,它以一行require_once('lib/setup.php')结束。这样:

  1. 通过require_once加载所有Moodle标准库;
  2. 开始处理会话;
  3. 连接数据库;
  4. 初始化一系列全局变量,我们一会就将看到它们。

Line 2:检查用户是否登录

require_login();

这行使得Moodle利用管理员配置过的任何认证插件来判断,当前访问用户是否已经登录。

一个与Moodle整合性更好的插件会在这里传递更多的参数,比如这个页面属于哪个课程或者活动。然后调用的require_login仍然会检查是否当前用户是否参加了这门课程或者活动。如果是,用户就可以访问这门课程,或者观看这个活动;如果不是,那么适当的错误信息会被显示出来。

13.2. Moodle中的角色和权限系统

接下来的两行代码显示出如何检查用户是否有做某件事的权限。正如你所见,从开发者的角度来说,这些API都十分的简单。但是,实际上在这下面是一个非常复杂的接入系统。这会给管理员很大的伸缩性,以控制什么人可以做什么。

Line 3: 获得上下文

$context = context_system::instance();                        // 3

在Moodle中,同一个人可能在不同的地方拥有不同的权限。比如说一个用户可能在某个课程上做一名老师,也可能是另一门课程的一位学生。这些地方被称作为上下文(context)。上下文在Moodle中构筑了一个特别像文件系统中目录结构那样的多层结构。

在系统的上下文中,有许多的上下文信息被构造出来,它们负责维护那些为了组织课程而被创建的不同分类(Category)。这些上下文可以是嵌套的,比如在一个分类里面包含有其他更多的分类。分类上下文同时也包含着课程上下文。最后,每一个课程中的活动也会拥有一个自己的Moodle上下文。

以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号