從頭學習Drupal--基本概念一

從頭學習Drupal--基本概念一
節點(Node)
我們知道Drupal是一個內容管理系統(CMS), 而一般我們所管理的內容, 就是新聞或博文之類的文章; 在通常意義上, 這些就是Drupal中的節點, 但Drupal並不是只能管理文章類內容, 它對所管理的內容進行了抽象, 形成了節點的概念. 如果我們學過面向對象的知識, 那我們就知道節點其實就是Drupal這個系統所管理內容的虛基類.
Drupal的核心定義了節點這個對象的各種抽象行爲和基本屬性, 同時它頁實現了基於文本方式來表達內容的兩種節點類型, :Page 和 Story. 自然節點類型就是節點的具體實現了, 也就是節點的子類. 雖然Drupal的核心僅實現了簡單的文本描述節點, 但它允許你可以自己擴展更多的節點類型. 目前Drupal社區已經有很多擴展模塊實現了圖像或視頻內容的管理, 同時也有大名鼎鼎的CCK模塊允許你定義更爲節點的更多細節, 這裏我們略過不表, 下面爲了更進一步瞭解節點的概念, 我們到數據庫(node表)看看它的主要字段:


節點ID, 自動增加的唯一索引;
版本ID, 用來追溯內容修訂;
屬性 比如發佈狀態, 語言, 類型等等;
操作信息 創建或修改時間, 操作者等;
內容 標題部分, 內容部分, 可能有的節點類型沒有此實際數據; 

評論(Comment)
評論雖然也是文章形式, 而且它和普通文章也很象, 但實際上它不是一種節點類型, 它被實現爲一種依附於節點的用戶反饋機制. 它與節點是否存在本質區別, 是否有必要分成兩類對象, 這個好像也一直在討論之中, 不知道在Drupal 7中會不會把它合入節點框架中. 但我個人認爲, 目前Drupal似乎還沒有節點與節點發生關聯的情況, 這樣把評論與節點分開是非常清晰的, 如果後續有此類需求, 則把評論納入節點管理是順理成章的事.

分類(taxonomy)
形象的說就是給節點分類, 它由一系列術語表組成, 每個術語表可以定義n多的術語, 這樣你可以用術語對你的內容進行標記, 從而達到分門別類的作用. 當然你的分類是否合理, 是否有效, 還得看你的術語表定義了. 比如說一篇技術文章, 你可以從其使用的技術領域來劃分, 也可以從其應用的領域來劃分, 甚至可以從作者的性別進行劃分. 總之, 分類就是個門綱目科屬的工作, 不過這可不是個簡單的活, 記得專門好像還有圖書管學來專門研究圖書的分類呢.

邊學習邊總結, 又補充一個分類的概念, 因爲這個也是Drupal核心中針對內容的相關概念 -- xeopn


從頭學習Drupal--基本概念二
新手專區
Drupal6.x 
由 xeopn 於 星期四, 2008-06-19 23:54 發表 

從頭學習Drupal--基本概念二

區域(Region)
這個概念其實只涉及到表現層, 簡單來說就是把頁面進行劃分, 分成一塊塊獨立的空間, 比如裝修做二室一廳, 那就有廳, 廚房, 衛生間, 主臥, 輔臥等, 當然頁面不是毛坯房了, 一般它分爲五個區域: header, footer, conten, right sidebar, left sidebar.

區塊(Block)
在Drupal的世界裏, 什麼東西都是圍繞節點(Node)展開的, 那節點又是爲誰呢, 當然是用戶, Drupal的用戶管理並沒有太多特別的地方, 所以我們也就不談了. 繞了半天, 到底談嘛嗎?
"談理想", "談戀愛", 錯嘛, 我們還是談節點. 用戶訪問你的網站幹嘛? 想看點內容嘛, 內容是嘛嗎, 是節點. 那光看節點嗎? 打比方說了, 你肚子餓了進飯店, 你光叫2碗米飯麼? 當然還得點幾個小菜, 興致到了再喝點小酒, 在Drupal眼裏, 這些其他的就是區塊咯. 可能有較真的看官說話了, 小菜和酒也都可以看做是一種派生內容類型(Content Type)啊, 它們也都可以看作節點啊. 那就借一步說話了, 飯店的招牌, 牆上的推薦菜, 你點菜看的菜單, 這些圍繞在你吃的主題周圍的露出和諧笑容的東東那就是區塊(Block). 某書有云: a block is a chunk of auxiliary information that is displayed on a page alongside the main page content.
放到我們經典的文章頁面上來看: 中間大塊的是文章內容, 而在其他區域也放着東西呢, 比如一個站點菜單, 最新評論列表, 日曆等等. 在Drupal中, 這些附加的輔助性的信息就是區塊.
區塊定義在數據庫表(block)中, 從表結構可以看出, 區塊由bid標識, 與模塊(Module)和主題(Theme)都是強關聯, 甚至Drupal已經爲block表建立了tmd(theme module delta)唯一索引. 其實block也是一種輔助的內容形式, 所以它由模塊定義實現, 而block只能算是頁面上的可選元素, 所以在展現上, 它必須與主題掛鉤, 以決定是否在該主題中被顯示. 同時區塊還與角色勾三搭四, 以便於頁面顯示信息與用戶掛鉤.

菜單(Menu)
從實質上說, 菜單就是一個區塊(Block), 當然菜單擴展了很多屬性和行爲, 使得它成爲Drupal中一個非常強大的導航系統. 可以說當一個區塊成爲菜單後, 它就不是一個人在戰鬥了... 這個暫時霧裏看花, 先不深究.




從頭學習Drupal--基本架構一
新手專區
Drupal6.x 
由 xeopn 於 星期五, 2008-06-20 16:05 發表 

前面學習了Drupal的一些基本概念, 其實我們在構建一個系統的時候, 一般都需要從兩個方面來考慮問題:


業務模型

也就是領域模型, 是面向我們所要解決的問題域所構建的模型, 前面我們說的關於內容描述方面的幾個概念, 其實就是對領域內概念,元素進行概括,抽象得出的業務模型基類. 構建良好的業務模型, 能有效地將問題域中的對象進行分類,綜合, 理清他們間的關聯, 闡明他們相互間的協作, 併爲最終形成系統的對象數據模型打下基礎. 我們所說的面向對象(OO), 其實主要就是一個建模的思想.


系統架構

它是從系統的實現角度出發的, 涉及的概念更多, 它主要是用來解決系統如何構建, 以實現業務的需求, 它還涉及系統的健壯性, 性能, 可擴展性, 可操作性, 可獲得性等一些其他質量屬性. 比如經典的MVC架構, WEB的二層, 三層, 四層架構, 這些都是架構實現的一種形式.


這兩方面相輔相成, 任何一方面設計不好都將極大的影響系統的質量(Quality). 很多人在系統設計時出於某種主觀或客觀原因要麼從單方面考慮, 要麼是把兩者混淆起來考慮, 這都不是好的系統設計方法, 都有很大的侷限性.

說了這麼些沒人感興趣的東西, 還是回到我們的主題, Drupal的架構. 其實我目前還是粗略看了Drupal, 主要是爬爬洋文, 大概看了一下bootstrap的代碼; 所以下面的描述主要關注Drupal的主體框架, 對其內部不進行深入描述(能力問題, 象國足看齊); 而且因爲我本身的多語言支持(Internationalization)實現的並不太好, 這裏記錄下來的就是僅我自己的理解, 供你們參考和指正.

什麼是Drupal的架構?
簡單的說, Drupal是一個基於B/S架構的內容管理系統(CMS), 它用PHP語言實現, 並以關係數據庫爲存儲機制. 與其說它是一個CMS, 不如說它是一個CMS框架更好. 單純做博客(Blog), 它不如WordPress簡單快速; 直接當CMS, 它不如Joomla美觀方便, 甚至不如某些國產, 但其實經過擴展, Drupal能夠做得與它們一樣好, 甚至更好, 這就取決於Drupal幽雅的架構設計. 這裏我爲啥用幽雅不用優雅呢? 其實就在於Drupal的具體實現, PHP本身是支持面向對象(OO)的, 但Drupal卻爲了兼容老版本並沒有使用, 而是通過一系列的約定俗成的編碼約定(Convention)來達到類似的效果. (期待Drupal 7的到來...)
好了, 下面是一個我簡單畫的Drupal的系統架構圖.
Drupal系統架構圖
我們看到, Drupal主要是一個三層結構: 表現層, 邏輯層和數據持久層. 顧名思義, 數據持久層主要是處理數據的持久化, 它是領域模型在具體數據庫中的實現; 而邏輯層則基於領域模型進行數據的業務邏輯處理, 它是整個結構的核心; 表現層則側重於領域數據的呈現和工作流在用戶側的控制表現, 它主要基於以用戶爲中心來設計(UCD).
邏輯層, 包括兩部分:Drupal的核心庫和模塊組(Modules).
核心庫主要包含Druapl的請求流程(Bootstrap)和一系列常用的公共支撐庫, 比如數據庫抽象接口, 多語言支持, 郵件處理, 圖像優化等. 當然還有最關鍵的鉤子(hook)框架, 鉤子是貫穿Drupal核心的一個重要特性, 它使得Drupal能高效靈活地協調模塊們的工作. 這裏把它叫做核心庫確實不妥當, 以爲它不僅僅是一個庫, 更是一個公共框架.
模塊就是Drupal的功能組件, 它處理具體的業務邏輯, 模塊如何劃分, 取決於你的業務劃分, 你的工作流, 你的設計思想.Drupal安裝包裏包涵了10多個模塊, 但它最核心的只有5個模塊: Block, Filter, Node, System, User. 如果說核心庫是劉備的話, 那這五個就是他的五虎上將了. 看看他們的功能吧, System使得系統具備了系統管理的能力, User使得系統具備了安全管理的能力, Node+Block+Filter使得系統具備了基本內容管理的能力.
天哪, Drupal難嗎, 真的不難, 這就是一個系統的完整原型, 這架構就是多少人心目中的完美架構啊! 從這裏出發, 你就能實現任何你想要的了, 想要什麼就實現個什麼模塊, 什麼自己做不了, 怕什麼, 社區裏有的是人在做, 拿來主義就行啊.
表現層, 有多少人是衝着Drupa的l外觀來的, 應該不多吧, 你要真衝這個來, 還真得勸你去選個別的CMS, 沒必要和自己驕矜.
但是,對一個CMS系統來說, 可定製的外觀是必須提供的能力的. 因此, Drupal提供了相當強大的內容表現擴展機制--主題(Theme)系統, 真的是很黃很暴力, 它包含主題引擎和主題兩個子層, 允許開發者全方位控制內容的表現. 系統對最終用戶的輸出, 你可以從主題層輸出, 也可以從主題引擎層輸出, 還可以直接從邏輯層裏的模塊層輸出; 什麼, 你不想實現表現層? OK, 完成一個機機接口的純後臺系統也是你的選擇. 不過太靈活並不一定是好事, 特別在一個具體的項目開發過程中, 記住, 制定必要的開發規約是保證項目質量和進度的有效手段.
還是回到正題, 雖然Drupal的整個主題機制強大複雜, 但在機制的最頂層--主題, 卻並不複雜. 它簡單到由一系列的模板文件, CSS文件, JS文件和圖片即可定義出美侖美奐的外觀.

持久層, 支持關係型數據庫, 模塊一般通過核心的數據庫API訪問, 你想直接訪問數據庫也支持, 但最好還是通過API, 這樣便於遷移和擴展.




從頭學習Drupal--基本架構二
新手專區
Drupal6.x 
由 xeopn 於 星期六, 2008-06-21 00:11 發表 

前面說了, Drupal的邏輯層由一個核心框架和一系列的功能模塊(Module,可以看成插件)構成, 框架與模塊間的協調就是通過鉤子機制來實現, 所以鉤子機制是Drupal模塊化系統的關鍵。什麼是鉤子機制, 它與面向對象的接口(Interface)類似, 就是爲規範定義了兩個實體間的功能界面, 使得實現了該界面的實體能進行交互. 在Drupal裏, 鉤子就是Drupal框架與模塊間進行交互的接口, 但由於Drupal沒有面向對象的機制, 所以它採用編程規約來討巧地實現.
在Drupal裏, 鉤子就是一系列需要實現特定功能的函數, 它們約定好了輸入參數和返回類型, 同時約定以xxx_yyy() 的形式來命名, 其中xxx是要實現功能的模塊名稱,而yyy則是具體的鉤子名稱 . 舉個例子, 你就更清楚了. 例如我有個模塊叫myexamplemodule, 現在我想在footer中添加一些Javascript代碼, 於是我在我的模塊代碼中實現一個叫myexamplemodule_footer()的函數, 在此函數中我實現了我想要功能; 於是用戶請求頁面時, Drupal的框架會檢測到我的模塊有myexamplemodule_footer()函數, 並且它會自動調用它, 是不是很簡單明瞭呢! Drupal核心框架的這種調度機制, 屏蔽了用戶自定義模塊與框架間的實際交互, 簡化了的開發, 因爲你只要實現一個函數即可, 別的事情繫統已經替你完成了. 更多的hook請查看Drupal的文檔庫.

Hook機制主要在模塊管理(includes/module.inc)中實現, 主要有的四個主要函數, 非常簡單.

  • module_hook($module, $hook)

  • 判斷某模塊是否實現某鉤子

  • module_implements($hook, $sort = FALSE, $refresh = FALSE)

  • 判斷哪些模塊實現某鉤子, 返回模塊名數組

  • module_invoke()

  • 調用具體模塊的具體鉤子, 變長參數, 參數包括模塊名, 鉤子名, 還有鉤子函數的參數

  • module_invoke_all()

  • 調用所有模塊的具體鉤子, 變長參數, 參數包括鉤子名, 鉤子函數的參數

下面看一下module_invoke_all()函數的具體實現, 該函數會在Bootstrap過程中被框架調用.



<?php
function module_invoke_all() {
       $args = func_get_args();
       $hook = $args[0];
       unset($args[0]);
       $return = array();
       foreach (module_implements($hook) as $module) {
          $function = $module .'_'. $hook;
          $result = call_user_func_array($function, $args); 
          if (isset($result) && is_array($result)) {
              $return = array_merge_recursive($return, $result);
          }
         else if (isset($result)) {
             $return[] = $result;
         }
  }
  return $return;
}
?>
Drupal的鉤子實現起來也比較靈活, 不想實現某鉤子, 不寫那個函數即可, 這樣可以減少PHP文件的代碼, 提高效率. 不過看起來還是有些龐大的感覺, 因爲只要實現了該鉤子函數的模塊, 就會被調用, 這樣應該會導致在輸出某內容時, 系統還會加載並執行其他無關的模塊. 總之, 粗淺一看, 這裏應該是個性能瓶頸.

 


從頭學習Drupal--基本架構三
新手專區
Drupal6.x 
由 xeopn 於 星期六, 2008-06-21 20:38 發表 

菜單(Menu)
前面曾經簡單提到過Drupal的菜單, 今天稍微深入來探討一下. 菜單能用來顯示導航信息, 我們安裝的系統, 默認安裝有3個菜單, 讓我們查看一下數據庫吧, 以menu_開頭的總共有三張表: menu_custom, menu_links, menu_router. 其中menu_custom表存放菜單定義信息, 但想知道他們都是由哪個模塊定義的麼? 別忘記了菜單如果要顯示就是區塊哦, 打開區塊表(blocks)看看吧. Here it is! 用戶模塊(User Module)定義了Navigation菜單(沒看數據庫我以爲是系統模塊(System Module)定義的呢), 菜單模塊(Menu Module)定義了Primary links和Secondary links兩個空菜單. 所以從表現層來看, 一個菜單就對應一個區塊(Block), 它被放置在頁面的某個區域(Region)來顯示給用戶進行導航.
其實Drupal的菜單機制不僅要能把導航顯示給用戶, 更重要的是在用戶點擊這些導航的時候, 能夠準確快速定位到相應的業務邏輯. 有人會問, 難道這也是個問題嗎? 要知道導航其實都對應他們具體的URI, 而傳統的URI的定位是先按目錄結構找處理文件, 然後根據Request參數對應業務邏輯,同時還要在業務邏輯中判斷用戶權限; 而Drupal有一套自己的內部路徑, 它是基於模塊化構建的, 與目錄結構一點關係也沒有了, 所以必須要有一套機制能在URI和業務邏輯間進行映射, 而Drupal的菜單機制就是完成這項工作的, 用戶點擊菜單項鍊接時, Drupal解析出內部路徑, 並根據內部路徑找到對應的業務邏輯, 並再完成判斷權限後轉交給業務邏輯進行處理, 這個過程Drupal稱之爲分發.
Drupal核心框架中的菜單api(includes/menu.inc文件)實現了上述功能, 它成功地解決了動態URL路徑到具體執行函數間映射, 對用戶屏蔽了系統預定義的Request參數的複雜處理, 在路徑和功能建立了必由之路. 啥也別說了, 看看分發函數的實現:


<?php
>
function menu_execute_active_handler($path = NULL) {
  if (_menu_site_is_offline()) {
    return MENU_SITE_OFFLINE;
  }
  if (variable_get('menu_rebuild_needed', FALSE)) {
    menu_rebuild();
  }
  if ($router_item = menu_get_item($path)) {
    if ($router_item['access']) {
      if ($router_item['file']) {
        require_once($router_item['file']);
      }
      return <strong>call_user_func_array($router_item['page_callback'], $router_item['page_arguments']);</strong>
    }
    else {
      return MENU_ACCESS_DENIED;
    }
  }
  return MENU_NOT_FOUND;
}
?>

 

用戶URL請求到達後, Drupal先進行Bootstrap初始化, 然後調用分發函數menu_execute_active_handler, 該函數根據解析出的內部路徑, 在系統構建出的菜單路由表中查找, 如果找到則判斷可訪問權限, 然後調用路由表中對應路徑註冊的Pange_callback回調函數, 這樣就完成一個URL請求到具體頁面邏輯的過程. 流程非常簡單清晰, 學過計算機原理, 熟悉中斷調用的對這流程應該都非常熟悉.

菜單路由(Menu Router)
Drupal系統主要依據menu_router表構建系統菜單路由, 而menu_router表的內容則是基於各模塊的hook_menu鉤子來獲得, 這個鉤子較少被調用, 一般都在模塊初始化或其他菜單需要重建的情況. 下面我們選一段Book模塊的menu鉤子代碼來看看:


<?php
function book_menu() {
  $items = array();
  $items['admin/content/book/%node'] = array(               
    'title' => 'Re-order book pages and change titles',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('book_admin_edit', 3),
    'access callback' => '_book_outline_access',
    'access arguments' => array(3),
    'type' => MENU_CALLBACK,
    'file' => 'book.admin.inc',
  );
}
?>
book模塊所定義的所有菜單路由表由一個二維數組表示, 其中每一項爲一個菜單路由, 下標即爲該路由的入口路徑, 這裏爲'admin/content/book/%node'. 菜單路由項的各屬性看命名已經比較清晰了, 想了解可以參考Drupal.
我們看上面用的路徑中包含一段'%node', 對了這是使用了通配符, 比如你訪問admin/content/book/7的時候, 會自動把node7裝載進來, 太多細節, 就此打住.

 

菜單項(Menu Item)
保存在menu _links表中, 定義了每個條目的名字, 條目間父子關係, 對應路徑, 所屬模塊等等很多屬性, 它應不像菜單路由項一樣隱藏在背後幹活, 它是可見的; 同時由於有通配符的存在, 它與菜單路由項並不是一一對應關係. (比較奇怪的是默認的Navigation菜單是用戶模塊創建的, 但它裏面的所有菜單項卻是系統模塊定義的.) 既然router表的數據從鉤子函數而來, 那link表是否也有對應的鉤子呢, 實際上菜單項是由菜單模塊(Menu Moudle)進行管理, 通過GUI界面直接配置, 當然菜單API也有對應接口,比如menu_link_save(). ( 實在不行你直接寫數據庫也行, 那不就是hack菜單模塊了麼)

Drupal6.x增加了兩個alter鉤子函數對應這兩張表, 它們是hook_menu_alter()<--->menu_router, hook_menu_link_alter()<--->menu_links, 它們主要處理內容變化時的處理邏輯, 由Drupal_alter()函數調用, 看代碼註釋說這個函數非常Ugly, 要在7中把它解決掉. 頭暈了一天, 今天也沒有心思再看下去了.

總結:
現在我們有點明白Drupal的菜單機制了吧, 它主要由菜單api和菜單模塊組成, 提供一種框架, 使得其他功能模塊能過註冊菜單路由項, 並在分發過程中, 通過該菜單路由表完成用戶頁面請求(具體URL)到功能模塊業務邏輯的映射. 當然Drupal的菜單機制還有很多複雜特性, 來日方長, 有空繼續鑽研.

另:有一點不明白的是menu_router爲啥要用鉤子函數, 不能直接用初始數據庫腳本麼, 不過我也沒研究過安裝過程, 似乎好像沒有一個地方用了數據庫腳本, 有人清楚這一塊麼?


 


 
從頭學習Drupal--基本架構四
新手專區
Drupal6.x 
由 xeopn 於 星期三, 2008-06-25 00:48 發表 

主題(Theme)
主題是什麼呢? 其實這個詞已經被用的很泛了, 所有涉及外觀定製的地方都充斥着這個詞, 比如我們最常見到的windows桌面主題, QQ的界面皮膚等. 說白了主題就是外觀, 可定製主題就是允許用戶自定義應用的外觀.
Drupal的外觀也是由其主題(Theme)來表現的, 一個CMS只有強大的業務邏輯處理和擴展能力, 而沒有好的外表, 一樣會流失大量的用戶. 所幸Drupal的外觀表現機制同樣十分強大, 它允許網站開發人員爲其網站重新設計開發個性化主題, 同時Drupal社區還有大量共享的主題, 下載下來後, 解壓到站點對應的主題目錄(sites/all/themes,或sites/指定站點/themes)下即完成安裝, 在管理界面直接熱插拔應用主題, 非常方便.

主題機制(Theme System)
爲了支持這麼靈活,方便又強大的可插拔主題, Drupal有一整套設計實現, 我們叫它主題機制(Theme System), 它被設計用來分離界面元素(比如HTML, CSS等)與核心業務邏輯. 通過這種分離, 有效的減少界面元素對核心代碼的影響, 從而能使核心代碼儘快穩定; 同時更換新的界面也不需要去動複雜的業務邏輯, 便於分配給專業的界面設計人員去開發, 從而降低用戶界面這種需求易變性模塊的開發複雜性和依賴性. 下面我們就到Drupal的主題機制內部去探索一番.
Drupal的主題機制(Theme System)有由三大部分組成: 主題(Themes), 主題引擎(Theme Engines)和主題api(includes/theme.inc).
主題(Themes)主要包含一組文件, 系統可以利用這些文件能展示對應的外觀, 以獲得不同的用戶感受。一個主題主要由以下內容組成:模板文件, 樣式表(CSS), 圖像(images), Javascript文件等; 對於一個web設計者來說, 樣式表什麼的應該都比較熟悉, 但模板文件是什麼呢? 模板就是一個HTML片斷模子, 我們可以用它來生成有具體含義的HTML片斷. 舉個例子, 就好像一張空的賀卡, 你填上"xx生日快樂"就成了生日賀卡, 你寫上"情人節一起happy吧"那就成了節日祝福賀卡. 因爲一般人都比較聰明, 所以空的賀卡大家也知道哪該寫姓名, 哪該寫祝福語; 萬一碰到個比較傻的要寫怎麼辦, 於是聰明人可以先用鉛筆在該寫的作些提示標記, 這樣傻子雖然不聰明但在有提示的地方把原來的鉛筆標記替換成他自己的話還是能做到的. OK, 這個帶有鉛筆標記的賀卡我們就可以稱之爲一張賀卡模板.
Drupal的主題模板文件也是同樣道理, 它是一種帶有標記(Tags)的HTML模板, 系統能夠識別標記, 並根據標記進行內容替換, 生成實際的HTML頁面. 那我靠什麼識別標記? 當然就是看標記所用的語言了. 話說如果是美國傻子, 那我們就用英語寫標記, 日本傻子則用日語. 回到Drupal世界, 寫它的模板文件中標記的語言我們稱之爲模板語言(Template Language), Drupal6.x目前支持三種模板語言: php(默認), smarty, Xtemplate; 光有語言還不行, 還要有傻子的大腦來能識別這些語言啊, 那麼系統中對應解釋這些語言的工具就是主題引擎(Theme Engine).

Drupal默認的主題引擎是phptemplate, 它使用PHP語言, 直接內簽在Drupal中, 不存在任何外部依賴. 下面看一個phptemplate的模板例子:


<?php
// $Id: block.tpl.php,v 1.3 2007/08/07 08:39:36 goba Exp $
?>;
<div id="block-<?php print $block->module .'-'. $block->delta; ?>;" class="clear-block block block-<?php print $block->module ?>">
<?php if (!empty($block->subject)): ?>;
  <h2><?php print $block->subject ?></h2>
<?php endif;?>;
  <div class="content"><?php print $block->content ?></div>
</div>
因爲phptemplate引擎使用的模板語言是PHP, 則裏面php代碼部分都是標記, 如果我們傳入blcok變量{0,xx區塊,xx區塊內容}, 則經過主題機制(Theme system)處理後,上面這段模板代碼將生成如下HTML片斷:
<div id="block-book-0" class="clear-block block-book">
  <h2>xx區塊</h2>
  <div class="content">xx區塊的內容</div>
</div>
主題api
主題api提供的強大機制使得分離出去的主題層能有機地和核心協同工作, 完成內容的表現. 該機制又是通過鉤子(hooks)技術實現的, 看來hooks不僅僅用於模塊(Module)與核心的交互, 它被應用到所有需要與核心交互的場景. 與普通的業務邏輯類的鉤子不同, 主題鉤子(theme hooks)具備以下的特性:

 


主題鉤子是按顯示組件(components)來定義的, 即一個顯示組件定義一個鉤子名;
主題鉤子不僅能靠註冊函數實現, 還能靠模板形式實現, 通過模板文件名的命名規約, 能實現與函數鉤子類似的效果
主題鉤子不僅是只能在模塊中實現, 而且在表現層的主題和主題引擎都能實現; 這裏注意了, 前面我們只說了主題中的模板文件, 這些文件是無法實現鉤子函數, 而表現層中的鉤子函數在哪裏實現呢? 對了, 在主題目錄下的template.php文件中實現.
主題鉤子必須有默認實現, 要麼在模塊中實現, 即模塊默認實現, 要麼直接使用主題api中的theme_HOOK(), 它定義了核心默認的顯示組件的外觀.
主題鉤子是縱向的, 是覆蓋式的, 也就是說只有最上層的能起作用, 底下的都被屏蔽了, 這種鉤子實現是不是更像函數重載呢 


上面這張圖是從drupal.org網站下載的, 它清楚地描述了主題機制的核心架構. 整個網頁由幾部分組成, 它們的不同顏色代表從不同鉤子輸出, 其中a點輸出代表默認主題實現(核心和模塊的默認鉤子實現, 對應兩種青灰的顏色); b點是主題引擎的鉤子重載點, 主題引擎鉤子實現的輸出爲褐色; c點則是主題的鉤子重載點, 它的鉤子輸出爲紅色, 這三層的輸出最終形成了給終端用戶的網頁.

主題鉤子的註冊是在模塊的hook_theme()中實現的(一個業務鉤子), 而主題鉤子有兩種形式: 回調函數和模板文件, 但主題層鉤子形式必須與業務邏輯層(Core和Module)的形式保持一直, 下面我們分開進行闡述(下面的描述都基於PHPTemplate引擎):
(1)模板文件形式
模板文件允許你僅用css和的修改預定義的模板即可變換站點的外觀, 非常簡介和直接, 是我們自定義外觀的主要方式. 常用的模板文件主要有以下幾個:page.tpl.php; node.tpl.php; block.tpl.php; comment.tpl.php; box.tpl.php;

  • page.tpl.php

  • 該模板描述一個HTML頁面的主要元素, 包括, 和 元素, 它非常複雜, 大概能使用30多個Tag變量

  • node.tpl.php

  • 該模板負責node內容的顯示, 它的Tag變量都與node的屬性相關, 而page.tpl.php的$content變量實際就代表它

  • block.tpl.php

  • 該模板負責block的顯示, 它有一個變量$block

  • box.tpl.php

  • 該模板負責畫一個簡單的盒子, 原來好像被用作搜索結果和form, 但這個版本已經很少被使用了, 它與block有什麼關係我到目前還沒搞清楚

  • comment.tpl.php

  • 該模板負責comment的顯示, 其他沒啥好說的

當處理頁面顯示請求時, 區塊的內容, 節點的內容, 評論的內容等等都會先被放到區域(Region)中, 然後通過page.tpl.php中的區域變量$content, $header, $left, $right, and $footer等拼接成實際頁面. 所以爲了減少顯示數據的生成, 可以禁用區域內的block而不是僅僅修改page模板. 

前面說了, 模塊也可以註冊模板鉤子, 主題也可以註冊模板鉤子, 那到底哪一個鉤子生效呢? 這是由主題機制的調度函數分發的. 下圖描述了幾種關鍵模板的查順序, 它們完全依賴模板名字的特殊命名約定來實現, 如果文件名相同, 則優先選擇上層的模板.

更詳細的關於模板文件內的Tag變量, 可以參考《Pro_Drupal_Development》和Drupal.org.

(2)回調函數形式
回調函數形式是一種用coding來處理自定義界面的方式, 說白了就是表現層沒剝離完全的那部分, 留了一個後門, 以便處理更特殊的顯示需求. 主題和主題引擎的鉤子函數都在template.php文件中實現, 它們的命名規範是mytheme_hook()和themeengine_hook(). 儘管採用回調函數, 比模板方式提高5倍的速度, 但我們在定製外觀的時候, 還是建議優先考慮定製模板鉤子. 如果確實沒有模板可用, 也儘量在主題引擎層實現函數鉤子, 這樣子主題也可以共享該函數鉤子.

最後, 整個上面的流程, 不論是模板還是回調函數, 全部都是靠主題api中的theme()函數來實現的, 該函數堪稱Drupal主題機制的中場發動機.

編後語:
我花了三天時間來看主題, 每多看一次都會對自己的否定幾次, 確實太複雜, 而且Drupal6主題變化很大, 每天都不停的修改這篇文章, 真的以爲這篇文章發不出來了, 但是爲了給我的學習進程做個見證, 在我還沒有完全理解它之前, 還是post出來了, 雖然不是很系統, 但它闡明我目前對主題機制的理解, 希望大家的指正.


 


從頭學習Drupal--基本架構五
新手專區
Drupal6.x 
由 xeopn 於 星期五, 2008-06-27 19:12 發表 

任何一個開放系統(Open System), 只要它與外界有接口, 就存在安全問題, 越是商業級應用越注重安全. 安全管理涉及很廣, 大到整個網絡安全的設定,小到具體按鈕的訪問, 如果你的系統哪天出現安全問題, 沒準追根溯源能找到機房看門老頭, 呵呵, 玩笑話略過, 今天我們主要看看Drupal的用戶權限管理, 也就是訪問控制系統.

權限管理的要素
我認爲, 一個權限管理系統主要由以下四要素組成: 訪問者, 管理對象, 操作和規則.


訪問者: 誰幹? who. 一般來說是人, 嚴格來說應該叫主動體, 這要看你的系統面向哪些用戶, 一般系統人機接口,機機接口都是存在的. 反正就是那些通過你係統開發的接口與你交互的東東.
管理對象: 幹誰? which. 就是被管理的東西, 比如倉庫裏的糧食, 博客系統的文章, 總之, 任何有管理要求的東西. 千萬要記住的一點是: 就算垃圾也是有管理要求的.
操作: 幹什麼? what. 對應具體操作, 比如寫文章, 運糧食, 倒垃圾...
規則: 怎麼幹? how, when, where, 定義操作實施的附加條件, 比如登陸時間等. 

權限管理的系統功能
可以說, 自從有了安全問題, 大家就在圍繞這幾個要素傷腦筋, 針對各種不同的管理需求, 也出現了各種不同的權限管理的方案, 如何在安全性, 操控性, 性能以及開發複雜性間取得平衡是恆古不變的主題. 爲了聚焦今天的話題, 我們主要從系統功能角度來窺視一下, 看看一個權限系統到底是如何工作的.


首先當然權限的定義, 即識別出系統中需要進行訪問控制的要素(管理對象, 操作, 規則), 並定義成相應的權限, 這是整個權限系統的核心, 針對不同的管理粒度和管理要求, 會出現截然不同的實現方式.
授權, 即把權限與用戶關聯, 最直接的方法是給用戶直接關聯, 而最常見最有效的方式是使用角色(也有叫組的)
鑑權, 根據規則執行權限的檢查, 以實施訪問控制, 看你的檢查點實現在哪呢, 同樣也是不同的要求有不同的實現
最後, 應該要能監控, 一般記日誌 

是不是比較清楚, 把這幾個系統功能考慮完全, 並實現好, 你的系統就安全了一半; 另一半是什麼, 是你的安全意識, 天乾物燥, 防火防盜, 只有真正樹立安全意識, 纔能有效地去應用各種安全措施, 防範危險. 不幸的是, 我們大多數人都疏予管理, 閒置, 不管不顧.

Drupal的權限管理框架
首先我們來識別系統中具備哪些要素: Drupal是一個基於web的CMS系統, 所以訪問者主要侷限在管理員和普通web訪問用戶(User); 而管理對象則是CMS中的核心--內容, 在drupal中它被抽象爲節點(Node). (應該還會有些其他的管理對象). 操作呢? 菜單, 按鈕, 在web系統裏是不是都對應到頁面, 也就是內部路徑(例如: "node/1/view"), 規則沒什麼特殊的: 操作時出現禁止頁面, 列表中不出現無權限內容等.

Drupa採用角色來關聯用戶和操作, 每個角色是從方便系統管理角度出發來定義的一類具備相同操作界面或行爲的訪問者, 但它不是簡單的用戶或操作的分組, 而是他們的有機結合, 形象的說就是一組權力的代名詞或者叫Permission Scheme. Drupall針對操作是否涉及到管理對象, 設計了兩個不同的鑑權流程, 以滿足不同的權限管理要求.

(1)與內容無關的操作權限
權限定義
鉤子hook_perm, 定義了每個模塊的操作權限, 用操作名描述.


<?php
/**
* Implementation of hook_perm().
*/
function book_perm() {
  return array('add content to books', 'administer book outlines', 'create new books', 'access printer-friendly version');
}
?>
授權
按角色授權, 權限被分配後, 存儲在數據庫表{permission}中.
鑑權
由函數user_access進行實際的鑑權工作, 它的參數是賬號信息與操作權限名, 返回ture或false. 你可以在任何需要的地方調用該函數進行鑑權, 但系統提供了一套默認的回調機制, 使得鑑權點實現簡單一致. 這就是菜單路由註冊函數中的access_callback參數, 該參數標明瞭此菜單路由需要進行訪問控制, 只有訪問回調函數驗證通過, 纔會被正確路由到對應頁面.
<?php
function forum_menu() {
  $items['forum'] = array(
    'title' => 'Forums',
    'page callback' => 'forum_page',
    'access callback' => '_example_test_access',  //這裏指定訪問驗證回調函數, 默認爲hook_access()鉤子, 
    'access arguments' => array('access content'),
    'type' => MENU_SUGGESTED_ITEM,
    'file' => 'forum.pages.inc',
  );
?>
鑑權鉤子, 模塊可以處理一些特殊的規則
<?php
/**
* Implementation of hook_access().
*/
function forum_access($op, $node, $account) {
  switch ($op) {
    case 'create':
      return user_access('create forum topics', $account);
    case 'update':
      return user_access('edit any forum topic', $account) || (user_access('edit own forum topics', $account) && ($account->uid == $node->uid));
    case 'delete':
      return user_access('delete any forum topic', $account) || (user_access('delete own forum topics', $account) && ($account->uid == $node->uid));
  }
}
?>
(2)節點訪問機制(Node Access System)
drupal還提供對管理對象的訪問控制--節點訪問機制(Node Access System), 這是一套複雜的機制, 我還沒完全摸清. 它主要是在權限定義方面進行了增強, 它通過定義用戶, 操作與節點的綁定, 來建立一個節點訪問表, 這樣當需要進行管理對象關聯鑑權時, 系統會執行node_access函數, 該函數會恰當地查詢節點訪問表獲得用戶的權限, 以完成鑑權.
我們再來看看這張節點訪問表, 用戶信息, 操作信息, 節點信息, OMG! m*n*P=? 如果還是按以前方式來定義, 那性能的低下可想而知, 所以綜合一般的權限控制需求, Drupal僅定義了三種的原始操作: view, update, delete, 而對於用戶則僅定義了一個抽象的二元組(realm, grant), 它與實際用戶間的映射由你自定義的節點訪問控制模塊行來決定, 這樣達到對用戶歸納的效果, 減小節點訪問表紀錄的數量級.
這套機制主要通過節點模塊(Node Module)提供的一系列api和hook_node_grants鉤子和hook_node_access_records鉤子來實現. 我們這次知道有這套東西即可, 不鋪開敘述.

 

Drupal的權限管理方法
1, 基本權限管理
儘管Drupal的權限管理機制是強大複雜的, 但其默認的基本權限管理卻非常簡單, 基於角色(role)和操作權限, 把操作賦予角色, 而把角色賦予用戶, 這樣用戶就能使用相應的權限. 基本權限管理粒度較粗, 只能滿足一般的安全需求.

2, 按內容類型鑑權
如果我們對管理對象有更精細的管理要求, 比如我想讓A角色能訪問page, B角色只能訪問story, 咋辦? 基本權限管理做不到了, 那我們就擴展模塊. 內容訪問模塊(Content Access )允許你對內容類型按角色設置權限(查看, 編輯, 刪除). 其實Drupal6.2基本權限管理裏也能設置按內容類型進行鑑權, 因爲drupal把所有節點類型的幾個基本操作都註冊了, 看下面這個醜陋的循環, 呵呵, 這幾個也是漢化不到位的地方.


<?php
function node_perm() {
  $perms = array('administer content types', 'administer nodes', 'access content', 'view revisions', 'revert revisions', 'delete revisions');
  foreach (node_get_types() as $type) {
    if ($type->module == 'node') {
      $name = check_plain($type->type);
      $perms[] = 'create '. $name .' content';
      $perms[] = 'delete own '. $name .' content';
      $perms[] = 'delete any '. $name .' content';
      $perms[] = 'edit own '. $name .' content';
      $perms[] = 'edit any '. $name .' content';
    }
  }
  return $perms;
}
?>

 

3, 按組鑑權
如果是個大公司, 有很多部門, 相互間都有安全需求, 比如:
1. 能按不同的部門創建不同的組, 它們都能管理其私有內容
2. 組成員擁有組內的權限
3. 跨組間允許指定某些特定的權限共享
4. 匿名用戶只能訪問標識爲"public"的內容, 註冊用戶能訪問public和Restricted, 僅組成員能訪問private等
看到這麼多需求, 則我們需要一個更復雜的權限管理, drupal目前有兩個擴展模塊都能提供類似的功能:
(a) taxonomy_access modules
使用drupal的分類模塊的基本功能, 完成以上的權限控制, 這裏有英文的教程, 步驟描述較清楚.
i, 定義一個詞彙表(Groups), 用來定義組(即公司部門); 把它應用到所有內容類型, 再添加2個條目(比如財務, 研發)
ii, 再定義一個詞彙表(Access), 用來定義訪問級別; 同樣應用到所有內容類型, 同時還要設置"必須"的選項, 這樣保證創建內容時必須選一個該
詞彙表中的條目. 加三個條目(public, Restricted, Private)
iii, 按基本權限管理的方法創建2個角色(財務人員, 開發人員), 再創建幾個用戶, 並分辨賦予對應角色(張三->財務, 李四->開發)
iv, 前面都是基本管理,沒涉及到擴展模塊, 現在打開"administer > user management > taxonomy access: permissions", 按照步驟開始設吧, 一個組鑑權系統完成, 祝你好運.

(b) 著名的OG
Organic Groups功能太強了, 上面的需求全部能滿足, 組或圈的概念是大型社區不可缺少的, 別猶豫了, 能用就用吧.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章