卷2:第13章 Moodle

2018-02-24 15:55 更新

作者:Tim Hunt
譯者:Li Shijian(李詩(shī)劍)

Moodle是一款為教育系統(tǒng)設(shè)計(jì)的Web應(yīng)用。我會(huì)對(duì)Moodle各個(gè)部分如何運(yùn)作做一個(gè)綜述,同時(shí)我將專注于介紹幾個(gè)我認(rèn)為特別有趣的設(shè)計(jì):

  1. 用插件分割應(yīng)用的方法;
  2. 權(quán)限系統(tǒng) —— 它控制著什么用戶可以在系統(tǒng)不同的地方做什么事情;
  3. 產(chǎn)生輸出的方式 —— 它使得應(yīng)用不同的主題來(lái)更改外觀,并且把界面接口分離出來(lái);
  4. 數(shù)據(jù)庫(kù)抽象層。

什么是Moodle?

Moddle?提供了一個(gè)師生之間的在線教學(xué)平臺(tái)。一個(gè)Moodle站點(diǎn)被劃分為不同的課程。特定的用戶可以以不同的身份參與到一門課程中去,比如學(xué)生或者老師。每一門課程都由一系列的資源和活動(dòng)組成。一個(gè)資源可以是一份PDF文檔,Moodle的一個(gè)HTML頁(yè)面,或者干脆是一個(gè)指向網(wǎng)絡(luò)上其他位置的鏈接。一個(gè)活動(dòng)可能是一個(gè)論壇,一次測(cè)驗(yàn),或者是一個(gè)Wiki。在一門課程中,這些資源和活動(dòng)以某種方式被組織起來(lái)。例如,它們可以按照邏輯上的話題,或者是日程上特定的周目被分配到一起。
圖13.2: 大學(xué)系統(tǒng)典型架構(gòu)

  • 一個(gè)管理跨系統(tǒng)的用戶帳號(hào)身份認(rèn)證服務(wù)(例如使用LDAP)。
  • 一個(gè)學(xué)生信息系統(tǒng)。它就是一個(gè)龐大的數(shù)據(jù)庫(kù),里面記載著所有的學(xué)生信息,包括他們當(dāng)前正在進(jìn)行的課程,以及以后需要完成的課程;還有他們的筆記,這份筆記可以是他們對(duì)一門所完成課程的高度總結(jié)。當(dāng)然,這個(gè)信息系統(tǒng)也可以提供其他的管理功能,比如跟蹤一個(gè)學(xué)生是否上繳了學(xué)費(fèi)。
  • 一個(gè)文檔庫(kù)(比如使用 Alfresco)。它用來(lái)存儲(chǔ)文件,以及跟蹤用戶合作維護(hù)文件時(shí)的工作流。
  • 一個(gè)電子檔案袋(ePortfolio)。 學(xué)生可以在這里存放他們自己的資料(assets)并且把它們組合起來(lái)形成其他文檔。例如利用這些資料編寫一篇CV(簡(jiǎn)歷),或者用來(lái)證明檔案所有者已經(jīng)滿足了一門實(shí)踐課的選修條件。

Moodle專注于為所有參與到教學(xué)中的人提供一個(gè)在線平臺(tái),而不是為某一個(gè)教育組織特別設(shè)計(jì)的某個(gè)系統(tǒng)。Moodle僅僅為非主要功能提供了最基本的實(shí)現(xiàn),所以它可以單獨(dú)的作為一個(gè)應(yīng)用,或者與其它系統(tǒng)進(jìn)行集成。Moodle扮演的角色被正式地稱為虛擬教學(xué)環(huán)境(VLE),或者是教學(xué)/課程管理系統(tǒng)(LMS,CMS,甚至LCMS)。

Moodle是一個(gè)用PHP編寫的開源免費(fèi)軟件(GPL)。它可以在絕大多數(shù)的Web服務(wù)器和平臺(tái)上運(yùn)行。它需要一個(gè)數(shù)據(jù)庫(kù),目前支持MySQL,PostgreSQL,MS SQL Server以及Oracle。

Moodle從哪里來(lái)?

Moodle項(xiàng)目由Martin Dougiamas在1999年開創(chuàng),當(dāng)時(shí)他正在澳大利亞的科廷大學(xué)工作。1.0版本于2002年發(fā)布,當(dāng)時(shí)使用的語(yǔ)言和數(shù)據(jù)庫(kù)版本是PHP 4.2和MySQL 3.23。那時(shí)的版本從一開始就限制了Moodle可能采用的框架。然而從那以后,整個(gè)軟件發(fā)生了翻天覆地的變化?,F(xiàn)在發(fā)布的版本是Moodle 2.2.x系列。

13.1. Moodle運(yùn)作方式綜述

安裝Moodle的三個(gè)部分

Moodle安裝由三部分組成:

  1. 代碼,通常在一個(gè)類似?/var/www/moodle?或者?~/htdocs/moodle?的目錄里。Web服務(wù)器應(yīng)該對(duì)這個(gè)目錄具有寫權(quán)限。
  2. 數(shù)據(jù)庫(kù),由上面提到過(guò)的幾種RDMS(RDBMS,關(guān)系性數(shù)據(jù)庫(kù)管理系統(tǒng))管理。實(shí)際上,Moodle給所有的表名增加了一個(gè)前綴。所以如果需要的話,它可以和其他應(yīng)用共用一個(gè)數(shù)據(jù)庫(kù)。
  3. moodledata?目錄。這個(gè)目錄用于存儲(chǔ)用戶上傳的文件以及系統(tǒng)生成的文件,同樣Web服務(wù)器需要有對(duì)這個(gè)目錄的寫權(quán)限。出于安全考慮,這個(gè)目錄應(yīng)該設(shè)置于Web根目錄之外。

以上三部分可以完全部署在一臺(tái)服務(wù)器上?;蛘撸捎秘?fù)載均衡的設(shè)置,在每臺(tái)Web服務(wù)器上都部署代碼,但是僅僅共用一個(gè)數(shù)據(jù)庫(kù)和一個(gè)很有可能在其他服務(wù)器上的moodledata目錄。

當(dāng)Moodle安裝完畢后,這三個(gè)部分的配置信息被存儲(chǔ)在moodle根目錄下的config.php文件中。

請(qǐng)求調(diào)度

Moodle是一個(gè)Web應(yīng)用,所以用戶通過(guò)瀏覽器來(lái)與之交互。從Moodle自己的視角來(lái)看,這就意味著它要響應(yīng)HTTP請(qǐng)求。Moodle的一個(gè)重要設(shè)計(jì)考量就是URL的名字空間,以及URL如何被調(diào)度到不同的腳本上。

Moodle在這里采用PHP標(biāo)準(zhǔn)方法。瀏覽一個(gè)課程的主頁(yè)時(shí),URL可能像?.../course/view.php?id=123,這里123就是這門課程在數(shù)據(jù)庫(kù)中的唯一標(biāo)識(shí)。瀏覽一個(gè)論壇討論時(shí),URL可能是.../mod/forum/discuss.php?id=456789。也就是說(shuō),這些特定的腳本,course/view.php?或者mod/forum/discuss.php?會(huì)來(lái)處理這些請(qǐng)求。

這對(duì)于開發(fā)者來(lái)說(shuō)是非常簡(jiǎn)單的。想要理解Moodle是怎么處理一個(gè)特定的請(qǐng)求,你只需要觀察URL,從閱讀那份php文件的代碼開始。但是從用戶的角度來(lái)看這是十分丑陋的,因?yàn)檫@些URL是永久不變的。比方說(shuō)一個(gè)課程改了名字,或者一個(gè)管理員把一個(gè)討論轉(zhuǎn)移到另一個(gè)論壇中,這些URL都不會(huì)變。(這對(duì)于URL來(lái)說(shuō)是一個(gè)非常好的性質(zhì),正如Tim Berners-Lee在他的文章Cool URIs don't change中提到的)

另一種可以采用的方法是建立一個(gè)唯一入口?.../index.php/[其他使請(qǐng)求唯一確定的信息]。這個(gè)單獨(dú)的index.php腳本會(huì)通過(guò)某種方式將請(qǐng)求進(jìn)行調(diào)度。這個(gè)方法添加了一個(gè)大多數(shù)軟件開發(fā)者都喜歡用的間接層。缺少了這個(gè)間接層并不會(huì)影響到Moodle的使用。

插件

和許多其它成功的開源項(xiàng)目一樣,Moodle由許多和系統(tǒng)內(nèi)核協(xié)同工作的插件構(gòu)建起來(lái)。這是一個(gè)絕妙的主意,因?yàn)樗梢允褂脩舭凑账麄兌ㄖ频姆椒▉?lái)增強(qiáng)Moodle的功能。一個(gè)開源系統(tǒng)的重要優(yōu)勢(shì)在于,你可以根據(jù)自己的特定需求來(lái)更改它。然而,為代碼增加高可定制性的同時(shí),會(huì)在系統(tǒng)升級(jí)的時(shí)候引入大麻煩,即使我們已經(jīng)采用了很好的版本控制系統(tǒng)。Moodle的插件通過(guò)定義好的API與內(nèi)核交互,所以在自包含的插件中,可以允許盡可能多的用戶定制與新特性被開發(fā)出來(lái)。這也方便了用戶根據(jù)需求定制自己的Moodle,分享這些定制內(nèi)容,同時(shí)也便于對(duì)Moodle系統(tǒng)內(nèi)核進(jìn)行升級(jí)。

有許多不同的方法可以將一個(gè)系統(tǒng)構(gòu)建成插件化的。Moodle具有一個(gè)相對(duì)龐大的內(nèi)核,并且插件是強(qiáng)類型的。我所說(shuō)的相對(duì)龐大的內(nèi)核,指的是內(nèi)核提供了大量的功能。這違反了那類,由一個(gè)小型的插件啟動(dòng)器進(jìn)行引導(dǎo),其余部分都是插件的架構(gòu)設(shè)計(jì)。

當(dāng)我提及插件是強(qiáng)類型的時(shí)候,我指的是根據(jù)你想要實(shí)現(xiàn)的具體功能,你可能需要寫完全不同的插件,實(shí)現(xiàn)不同的API。比如,一個(gè)新的活動(dòng)模塊插件會(huì)與一個(gè)新的認(rèn)證插件,或者是提問(wèn)插件截然不同。根據(jù)最后統(tǒng)計(jì),現(xiàn)在我們一共有35種不同的插件(這里有一個(gè)Moodle插件類型完全列表)。這違背了那類,所有插件通過(guò)使用最基本的API,通過(guò)注冊(cè)它們感興趣的鉤子和事件與內(nèi)核進(jìn)行交互的架構(gòu)設(shè)計(jì)。

通常來(lái)說(shuō),Moodle現(xiàn)在有嘗試把更多的功能移到插件中以減小內(nèi)核的趨勢(shì)。可是這并沒有帶來(lái)巨大的成功,因?yàn)楫?dāng)前一個(gè)逐漸增長(zhǎng)的特性集趨于去擴(kuò)展內(nèi)核。另一個(gè)趨勢(shì)是盡可能將不同種類的插件進(jìn)行規(guī)范化。這樣在許多公共功能上,比如安裝和升級(jí),所有類型的插件都能夠按照統(tǒng)一的方式運(yùn)行。

一個(gè)Moodle中的插件其實(shí)就是一個(gè)包含許多文件的目錄。每一個(gè)插件都有一個(gè)類型和名字,這兩個(gè)構(gòu)成了這個(gè)插件的"Frankenstyle"組件名稱。("Frankenstyle"這個(gè)單詞出自于開發(fā)者Jabber頻道的一次討論,人人都愛它,所以這個(gè)單詞就被固定下來(lái)了)插件的類型和名字決定了這個(gè)插件目錄的路徑。插件類型給定一個(gè)前綴,目錄名稱就是這個(gè)插件的名字。這里有一些例子:

| 插件類型 | 插件名稱 | 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 |

最后的一個(gè)例子表明了每一個(gè)活動(dòng)模塊被允許聲明子插件類型。只有活動(dòng)模塊才能做到這個(gè),出于兩點(diǎn)原因。首先如果所有的插件都可以聲明子插件類型,這或許會(huì)帶來(lái)嚴(yán)重的性能問(wèn)題。另外活動(dòng)模塊是Moodle中最重要的教育活動(dòng),也是插件中最重要的類型,所以它們應(yīng)該具有特殊的權(quán)限。

示例插件

我會(huì)以一個(gè)具體的插件實(shí)例來(lái)解釋Moodle架構(gòu)中的大量細(xì)節(jié)。作為慣例,我選擇實(shí)現(xiàn)一個(gè)顯示"Hello world"的插件。

這個(gè)插件實(shí)際上并不適合任何一種Moodle標(biāo)準(zhǔn)插件。它只是一個(gè)簡(jiǎn)單的腳本,和其他任何東西都沒有聯(lián)系,所以我選擇把它制作成一個(gè)'local'類型的插件。這是一個(gè)catch-all的插件類型,專門處理一些雜亂的功能,所以在這里再適合不過(guò)了。我給我的插件命名為greet,所以它的Frankenstyle的名字是local_greet,路徑為local/greet。(插件代碼下載)

每一個(gè)插件都必需包含一個(gè)叫做version.php的文件,這個(gè)文件定義了關(guān)于這個(gè)插件本身的元數(shù)據(jù)。Moodle的插件安裝系統(tǒng)會(huì)使用它來(lái)對(duì)插件進(jìn)行安裝和升級(jí)。例如local/greet/version.php包含代碼:

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

因?yàn)榭梢詮穆窂缴巷@然地推導(dǎo)出插件的名字,所以乍看之下代碼里面包含組件名(component name)略顯多余。而實(shí)際上,安裝器需要通過(guò)組件名來(lái)驗(yàn)證插件是否安裝在正確的位置上。版本(Version)字段定義了這個(gè)插件的版本,成熟度(Maturity)是諸如ALPHA,BETA,RC(發(fā)布候選版, release candidate), 或者STABLE這樣的標(biāo)簽。Requires字段標(biāo)識(shí)著能和這個(gè)版本兼容的Moodle最低版本號(hào)。必要的話,你也要記錄下這個(gè)插件依賴的其他插件。

這里是這個(gè)簡(jiǎn)單插件的主要腳本(存儲(chǔ)在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:引導(dǎo)Moodle

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

這單獨(dú)的一行是大多數(shù)工作都要首先完成的。我之前說(shuō)過(guò),config.php包含著Moodle如何連接數(shù)據(jù)庫(kù)以及找到metadata目錄的細(xì)節(jié)。然后,它以一行require_once('lib/setup.php')結(jié)束。這樣:

  1. 通過(guò)require_once加載所有Moodle標(biāo)準(zhǔn)庫(kù);
  2. 開始處理會(huì)話;
  3. 連接數(shù)據(jù)庫(kù);
  4. 初始化一系列全局變量,我們一會(huì)就將看到它們。

Line 2:檢查用戶是否登錄

require_login();

這行使得Moodle利用管理員配置過(guò)的任何認(rèn)證插件來(lái)判斷,當(dāng)前訪問(wèn)用戶是否已經(jīng)登錄。

一個(gè)與Moodle整合性更好的插件會(huì)在這里傳遞更多的參數(shù),比如這個(gè)頁(yè)面屬于哪個(gè)課程或者活動(dòng)。然后調(diào)用的require_login仍然會(huì)檢查是否當(dāng)前用戶是否參加了這門課程或者活動(dòng)。如果是,用戶就可以訪問(wèn)這門課程,或者觀看這個(gè)活動(dòng);如果不是,那么適當(dāng)?shù)腻e(cuò)誤信息會(huì)被顯示出來(lái)。

13.2. Moodle中的角色和權(quán)限系統(tǒng)

接下來(lái)的兩行代碼顯示出如何檢查用戶是否有做某件事的權(quán)限。正如你所見,從開發(fā)者的角度來(lái)說(shuō),這些API都十分的簡(jiǎn)單。但是,實(shí)際上在這下面是一個(gè)非常復(fù)雜的接入系統(tǒng)。這會(huì)給管理員很大的伸縮性,以控制什么人可以做什么。

Line 3: 獲得上下文

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

在Moodle中,同一個(gè)人可能在不同的地方擁有不同的權(quán)限。比如說(shuō)一個(gè)用戶可能在某個(gè)課程上做一名老師,也可能是另一門課程的一位學(xué)生。這些地方被稱作為上下文(context)。上下文在Moodle中構(gòu)筑了一個(gè)特別像文件系統(tǒng)中目錄結(jié)構(gòu)那樣的多層結(jié)構(gòu)。

在系統(tǒng)的上下文中,有許多的上下文信息被構(gòu)造出來(lái),它們負(fù)責(zé)維護(hù)那些為了組織課程而被創(chuàng)建的不同分類(Category)。這些上下文可以是嵌套的,比如在一個(gè)分類里面包含有其他更多的分類。分類上下文同時(shí)也包含著課程上下文。最后,每一個(gè)課程中的活動(dòng)也會(huì)擁有一個(gè)自己的Moodle上下文。

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)