卷2:第13章 Moodle

2018-02-24 15:55 更新

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

Moodle是一款為教育系統(tǒng)設計的Web應用。我會對Moodle各個部分如何運作做一個綜述,同時我將專注于介紹幾個我認為特別有趣的設計:

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

什么是Moodle?

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

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

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

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

Moodle從哪里來?

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

13.1. Moodle運作方式綜述

安裝Moodle的三個部分

Moodle安裝由三部分組成:

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

以上三部分可以完全部署在一臺服務器上。或者,采用負載均衡的設置,在每臺Web服務器上都部署代碼,但是僅僅共用一個數(shù)據(jù)庫和一個很有可能在其他服務器上的moodledata目錄。

當Moodle安裝完畢后,這三個部分的配置信息被存儲在moodle根目錄下的config.php文件中。

請求調(diào)度

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

Moodle在這里采用PHP標準方法。瀏覽一個課程的主頁時,URL可能像?.../course/view.php?id=123,這里123就是這門課程在數(shù)據(jù)庫中的唯一標識。瀏覽一個論壇討論時,URL可能是.../mod/forum/discuss.php?id=456789。也就是說,這些特定的腳本,course/view.php?或者mod/forum/discuss.php?會來處理這些請求。

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

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

插件

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

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

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

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

一個Moodle中的插件其實就是一個包含許多文件的目錄。每一個插件都有一個類型和名字,這兩個構成了這個插件的"Frankenstyle"組件名稱。("Frankenstyle"這個單詞出自于開發(fā)者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架構中的大量細節(jié)。作為慣例,我選擇實現(xiàn)一個顯示"Hello world"的插件。

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

每一個插件都必需包含一個叫做version.php的文件,這個文件定義了關于這個插件本身的元數(shù)據(jù)。Moodle的插件安裝系統(tǒng)會使用它來對插件進行安裝和升級。例如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(發(fā)布候選版, 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

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

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

Line 2:檢查用戶是否登錄

require_login();

這行使得Moodle利用管理員配置過的任何認證插件來判斷,當前訪問用戶是否已經(jīng)登錄。

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

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

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

Line 3: 獲得上下文

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

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

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

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

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號