Moodle开发笔记2-Block开发

来源:互联网 发布:新手单反相机推荐知乎 编辑:程序博客网 时间:2024/06/05 00:13

Block是以一个长方形区域出现在moodlesitepage的左列或右列。Block是最简单、也是最常用的moodle plugin

 

下面讲解如何开发一个hello world moodle block “helloworld”

1. create “helloworld” folder (目录名来自你的modulename) in ”moodle/blocks” folder

 

2. ”helloworld”目录下创建blockpage “block_helloworld.php” (命名格式是 [module-type]_[module-name].php)

 

Block php file要创建一个同名的class,该class extends “block_base”class。同时需要有2个基本的函数:”init” and “get_content”

<?php

class block_helloworld extends block_base {

functioninit() {

$this->title= get_string('helloworld', 'block_helloworld');

$this->version= 2009050700;

}

functionget_content() {

if($this->content !== NULL) {

return$this->content;

}

$this->content= new stdClass;

$this->content->text= 'Hello World!';

return$this->content;

}

}

?>

 

Init函数至少要设置2个变量:block title andblock version

 

block title将会显示在block区域的最上端。

block version 注意:你每次upload你的plugin to moodle system,即将上传的version必须大于当前存在于moodleplugin versionversion的格式为:YYYYMMDD##。最后2位是YYYYMMDD当天的第n个版本。例如 2010062903,表示2010629日第3update version

 

get_content函数所返回值就会作为blockpage显示的content。同时它会存储在$this->content变量里。

 

以上2步就完成了最简单的helloworld block

如何激活?

admin account login,然后click “Notifications”in “Site Administration” blockmoodle 就会即时check and install new plugin

 

安装好之后如何使用?

admin account login,然后在你要添加helloworldpage里启动edit mode,然后in “Blocks” pull downlistselect “helloworld” block即可

 

 

上面的2步是必须的,下面的步骤是可选的

 

3. (Optional) Add language file(like java message properties file)。在”helloworld”目录下创建一个lang目录,然后在”lang”下创建一个”en_utf8,它表示这是一个language folder for Englist with Unicode encoding。如果你希望有一个for US 方言的englist language pack,那么就在”en_utf8”目录下创建一个en_us_utf8目录。Moodle里,child language会继承parent language的所有string message

 

然后在”en_utf8创建一个与block page同名的php file“block_helloworld.php”,然后把所有要用到的message都放到该文件里。其格式是把这些messages存储在”$string”数组变量里

 

下面一个language php file的例子:

<?PHP

$string['helloworld'] = 'Hello World';

$string['helloworld:view']= 'View Hello World Block';

$string['blockname']= 'HelloWorld';

?>

 

回顾一下步骤2init函数里用到一个函数

            get_string('helloworld','block_helloworld');

该函数Returnsa localized string

 

第一个参数对应的是language file里的message key,例如上例的第一个参数就是对应languagefile里的”helloworld”message

 

第二个参数是指定来自哪一个modulelanguage file,例如上例指定的是helloworldblock。该参数值实际就是存储该messagephp file name (不要.php extension)。例如:我们的language phpfileblock_helloworld.php,所以参数值为” block_helloworld”

 

另外还有一个函数与get_string类似,就是print_string,它是把返回的string 输出。

 

4. (Optional but very mportant) Working withcapabilitiescapabilities见上面的章节。

 

5. (Optional but very mportant) 添加helloworldblockconfiguration interface,使得在edit mode,在block区域里多一个button link toconfiguration interface

 

例子:使helloworld block具有configuration功能,使得可以设置$this->content的值

1)需要在block main page”block_helloworld.phpblock_helloworld class里添加一个returntrueinstance_allow_config函数,代码如下:

function instance_allow_config() {

return true;

}

 

2)在block root目录下创建一个config_instance.html file。该html file的代码实际上是php code

<?php print_textarea(true,10, 50, 0, 0, 'text', $this->config->text); ?>

<inputtype="submit" value="submit" />

<?php use_html_editor();?>

 

第一行是输出一个text area (nametext),其值为$this->config->text 注意:text areaname的值必须与$this->context里的对应的key name相同),在submit之后text area的值就会自动赋值给对应的$this->context里对应的变量。例如,该例的textareanametextsubmit之后该值就会赋给$this->context->text

 

第三行是嵌入Moodle's integrated WYSIWYG editor

 

3)修改block_helloworldclassget_content()函数,使得输出的content来自configuration interface设置的值。

            $this->content->text= $this->config->text;

 

 

6. (optional) add schedule function to block.

Moodlecron.php用来运行一个schedule来执行maintenance tasks,例如email delivery and backup。它可能每5~15分钟就运行一次。

 

例子:如果helloworld block需要执行schedule task,你需要

1) block_helloworld classinit函数里添加代码:

                        $this->cron = 5;

            这表示每5秒就会执行下面定义的cron函数一次。

 

2block_helloworld class里添加函数cron()

   function cron(){

print("Hello World is running its cronprocess./n");

}

 

 

参考书中的另外一个实例:Instructorcontact block

 

 

注意:如果我们想在moodle里添加一个不需要interfacemodule,那么Block就是最好的选择。对于没有interfaceblock$this->content->text andfooter要设置empty。如:

function get_content() {

if($this->content!== NULL) {

return$this->content;

}

$this->content= new stdClass;

$this->content->text= '';

$this->content->footer= '';

return$this->content;

}

 

 

 

官方moodle block开发的翻译

Ref link http://docs.moodle.org/en/Development:Blocks

 

以开发一个简单的”SimpleHtml” block为例来讲解。

 

Minimumrequirement of block

blocks目录下创建simplehtml目录,然后在该目录下创建block_simplehtml.php

<?php

class block_simplehtml extends block_base {
//init method必须设置var title and version 
function init() {
    $this->title   = get_string('simplehtml', 'block_simplehtml');
    $this->version = 2004111200;
}
 
//
function get_content() {
  //check content是否为null是因为moodle的内部机制在一个php page里可能对同一个blockinstance都会call几次,那么为了提高性能,我们只会在创建该block instance时(这时content=null)才为其赋值

    if ($this->content !== NULL) {
      return $this->content;
  }
  $this->content         =  new stdClass;
  $this->content->text   = 'The content of our SimpleHTML block!';
  $this->content->footer = 'Footer here...';
  return $this->content;

  }
}
?>

 

EnableConfigure block

首先在block_simplehtml class里添加下面的function使得该block可以configure

function instance_allow_config() {
  return true;
}

 

然后在simplehtml目录下创建一个config_instance.html文件,该文件包含当user点击blockconfiguration button时要显示的configuration pagecontent

<?php print_string('configcontent', 'block_simplehtml'); ?>:

<?php print_textarea(true, 10, 50, 0, 0, 'text', $this->config->text); ?>

<input type="submit" value="<?php print_string('savechanges') ?>" />

<?php use_html_editor(); ?>

 

上面的代码就是一个简单的configuration page你会奇怪怎么没看到<form>?对的,不需要写<form>,只需要提供你需要config的东东的input,以及一个submit button即可。当你click submit button时,moodle会自动波帮你存储config setting$this->config里。例如上例name为“text”的input field value就会存储在$this->config->textthis->config变量可以供block_simplehtml class里除了init() method之外的任何地方调用!

 

下面我们修改block_simplehtmlgetContent function来使用configuration varthis->config

function get_content() {

    if ($this->content !== NULL) {

     return $this->content;

    }

 

    $this->content =  newstdClass;

    $this->content->text   = $this->config->text;

    $this->content->footer = 'Footer here...';

 

    return $this->content;

  }

 

 

Specialists

我想在configuration page里设置blocktitle,则在config_instance.html里添加

<input type="text" name="title" 
        value="<?php echo $this->config->title; ?>" />

 

但由于$this->config变量不能在init() method里使用以及$this->title不能在getContent function改变,所以无法在init()里或在getContent method里使用下列代码

    $this->title   = $this->config->title;

 

怎么办?就要使用到specialization()method来给title赋值,该method是紧接着init()后被调用,即是在before block's content is computed for the first time之前被调用。

 

同时specialization() method is the natural choice forany configuration data that needs to be acted upon "as soon aspossible",即可以对this->config包含的值进行预处理。例如下列代码会在specialization methodcheck如果this->config->textempty,则给一个default value给它。

 

function specialization() {
  if (!empty($this->config->title)) {
    $this->title = $this->config->title;
  } else {
    $this->config->title = 'Some title ...';
  }
  if (empty($this->config->text)) {
    $this->config->text = 'Some text ...';
  }    
}

 

Now You See Me, Now You Don't

有时你可能想开发一个这样的block当有数据时,就显示该block,但当没有数据时,就隐藏整个block。典型的例子是”recentactivity” block,如果没有recentactivity,该block就会自动隐藏。

 

怎么实现这个功能?

很简单,只需要在get_content()里设置$this->content->text and$this->content->footerempty string即可moodle在处理block时,会先call is_empty() methodcheck it,如果text and footer is empty,就不会显示这个block

 

注意:不管该blocktitle是不是empty,也不管hide_header() methodhide or show header,都不会影响上述show/hide blockbehavior。即当content is empty时,即使title不为empty,也不hide headermoodle照样还是会隐藏该block

 

 

Allow multipleblock instance in a course

如果你希望在同一个course里添加同一个block的多个instance,则要在block class里添加instance_allow_multiple method

function instance_allow_multiple(){

  return true;

}

要注意的是

1.即使block本身supportmultiple instanceadmin还是可以通过Administration/Configuration/Blocks pagedisable multiple功能

2.如果“allow multiple”和“allow config”同时存在,则会自动disable allow config function

 

 

The Effects ofGlobalization

有时候admin可能希望他能够有一个地方对某个block的所有instance能一次性进行设置

 

例如,admin希望simplehtml blockcontent长度不要超过200字,或者content只允许plaintext,如果是html text,则filter html tag

 

首先,在blockclass里添加下列代码

function has_config() {
  return true;
}

 

然后,创建一个file config_global.html,该file is for the configuration screen output. 在该configuration page,我们添加一个checkboxsaying "Do not allow HTML in the content"以及一个"submit"button.

 

<input type="hidden" name="block_simplehtml_strict" value="0" />
<input type="checkbox" name="block_simplehtml_strict" value="1"
   <?php if(!empty($CFG->block_simplehtml_strict)) 
             echo 'checked="checked"'; ?> />
 <p>
 <input type="submit" value="<?php print_string('savechanges'); ?>" />
 </p>

 

上面的代码会global configure 变量block_simplehtml_strict,该变量的值在submit时会储存在$CFG->block_simplehtml_strict。注意设置的变量名一定要是唯一,如果别的module$CFG使用同样的变量名,那就惨啦。所以把变量命名为block_simplehtml_strict应该是ok的。

 

上面代码你会奇怪为什么有2input field(一个hidden[always 0], 一个checkbox)的name都为block_simplehtml_strict? This is a trick。因为如果没有hiddeninput field,当checkbox没有被勾上,该variable (block_simplehtml_strict=0)根本就不会passrequest。这样就无法设置$CFG->block_simplehtml_strict=0。因此加多一个hidden是为了保证request param里一定有block_simplehtml_strict variable

 

原理就是:当PHP处理formrequest时,会按the variablesform里的出现顺序进行处理。当遇到一个之前已经存在并处理了的variable,新值就会覆盖旧值。利用这一点,我们就把hidden "block_simplehtml_strict"=0放在前面,checkbox放在后面。如果没勾上checkbox,就会有hidden的值0,如果勾上,就会使用checkbox的值1

 

如果你觉得用2input field来使用同一个name太令代码confuse有一个替代方案:overwrite config_save() method (当在global configuration page click submitbutton时,就会调用它)

下面代码是缺省的config_save()method,其中参数$dataform request variable key value pair array

function config_save($data) {
  // Default behaviour: save all variables as $CFG properties
  // You don't need to override this if you 're satisfied with the above
  foreach ($data as $name => $value) {
    set_config($name, $value);
  }
  return TRUE;
}

 

我们重写它使得它会check是否存在block_simplehtml_strict variable. 如果不存在,就表示没有勾上checkbox使用这个方法就不需要添加hidden input field了,I like it.

function config_save($data) {
  if(isset($data['block_simplehtml_strict'])) {
    set_config('block_simplehtml_strict', '1');
  }else {
    set_config('block_simplehtml_strict', '0');
  }
  return true;
}

 

OKglobal confirmation page里设置了$CFG->block_simplehtml_strict,那在我们的simplehtml block里如何使用它?其实$CFG是一个超全局变量,任何地方都可以使用它

 

那么在本例中我们希望根据$CFG->block_simplehtml_strict这个block global configuration,来使得block instancecontent只能是plaintext,如果有html textfilter。这时我们要overwrite instance_config_save()method

instance_config_save()

该方法允许你override the storage mechanismfor your instance configuration data,即当在某一个blockinstanceconfigurationpageclicksubmit button时,就会先调用该方法,你可以重写它来对submit formdata进行处理,处理完的最后再call parent::instance_config_save($data)来调用父类缺省的instance_config_save method. 该方法的参数是一个associative array,它包含有submit formkey/value pair缺省的instance_config_save如下:

function instance_config_save($data) {

  $data =stripslashes_recursive($data);

  $this->config = $data;

  return set_field('block_instance', 'configdata', base64_encode(serialize($data)),

                      'id', $this->instance->id);

}

从上面缺省的方法可以看到,它会先调用stripslashes_recursive方法对submitted POST data进行stripslashes and recursive的处理,然后才调用set_field方法把处理后的data存到db里该blockinstance record"configdata" field

 

好了,现在我们通过重写instance_config_savefilter html tag

function instance_config_save($data) {
  global $CFG;
  if(!empty($CFG->block_simplehtml_strict)) {
    //filter html tag
    $data->text = strip_tags($data->text);
  }
  //call parent instance_config_save method
  return parent::instance_config_save($data);
}

 

 

hide_header()

如果你想hideblock title/header,那么添加下列方法到blockclass

function hide_header() {
  return true;
}

注意:moodle不允许在blockinit() method里把title设置为empty

 

 

preferred_width()

设置blockprefer width Moodle blockwidth的处理过程包括2 parts:首先moodlequerycourse page里的每一个blockprefer width,然后使用最大的prefer width作为desired value。因此当你设置prefer width时,moodle不一定保证一定以这个prefer width来显示,但显示该block时应该不会小于该prefered width

 

所有的standard Moodle course formatswill deliver any requested width between 180 and 210 pixels, inclusive. 当你使用下列代码时:

function preferred_width() {
  // The preferred value is in pixels
  return 200;
}

就会使你的blockwidth大于standard

 

 

html_attributes()

该方法是用来控制处理blockcontainer每一个block都会包含在<div> or <table> container里(即block html code会包含在<div>or <table>里)。那么该方法可以通过添加一些attributes来设置包含blockcontainer。具体方法有下列2

a)      directly affect the end result (if we say, assignbgcolor="black")

b)      container一个class,从而利用CSS来控制

 

下面的例子就用到第二种方法。

function html_attributes() {
  return array(
    'class'       => 'sideblock block_'. $this->name(),
    'onmouseover' => "alert('Mouseover on our block!');"
  );
}

以上代码把包住blockcontainer设置为class=sideblock block_xxx,这样我们就可以use that class to make CSS selectors in our themeto alter this block's visual style (for example,".sideblock.block_simplehtml { border: 1px black solid}").

 

另外把onmouseover赋予该blockcontainer,使得当鼠标进入该container时,就alert.

 

缺省的html_attributes()method

function html_attributes() {
    // Default case: an id with the instance and a class with our name
    return array('id' => 'inst'.$this->instance->id, 
                'class' => 'block_'. $this->name());
}

Ifyou intend to override this method, you should return the default attributes aswell as those you add yourself. The recommended wayto do this is:

function html_attributes() {
    $attrs = parent::html_attributes();
    // Add your own attributes here, e.g.
    // $attrs['width'] = '50%';
    return $attrs;
}

 

applicable_formats()

该方法是用来控制block可以在哪里使用。有些block可能开发者不希望在所有地方都能够使用,例如"Social Activities"block只适用于social formatcourse,对于weekformatcourse并不合适,因此block需要设置为只有socialformatcourse才能够添加该block,就要用到applicable_formatsmethod

 

注意:在applicable_formats method里定义哪些地方可以使用block,是以page为设置,而不是以course来设置。这是因为blocks can be displayed in anypage that supports them。例如,thequiz view page (the first one we see when we click on the name of the quiz)also supports blocks

1

function applicable_formats() {
  return array(
           'course-view' => true, 
           'course-view-social' => false);
}

上述代码先设置了允许所有的courseview.php page都可以添加该block,然后再disallow social formatcourseview page不能够使用它

 

2

function applicable_formats() {
  return array(
           'site-index' => true,
          'course-view' => true, 
   'course-view-social' => false,
                  'mod' => true, 
             'mod-quiz' => false
  );
}

上述代码设置了a blockcan be displayed in the site front page, in courses (but not social courses)and also when we are viewing any activity module, except quiz

 

3

function applicable_formats() {
  return array('site' => true);
}

 

上述代码设置了a block appear onlyin the site front page (如果省略page name,则看作是index page,即site = site-index)

 

从上面的例子可以看出page format name的格式定义方法 例如,

l 如果你需要设置/course/view.php是否能够使用block,则format name of that page is course-view.

l 类似的,请理解a quiz view page is mod-quiz-view

定义format name的规则

  1. The format name for the front page of Moodle is site-index.
  2. The format name for courses is actually not just course-view; it is course-view-weeks, course-view-topics, etc.
  3. Even though there is no such page, the format name all can be used as a catch-all option.

We can include as many format names as we want in our definition of theapplicable formats. Each format can be allowed or disallowed, and there arealso three more rules that help resolve the question "is this blockallowed into this page or not?":

  1. Prefixes of a format name will match that format name; for example, mod will match all the activity modules. course-view will match any course, regardless of the course format. And finally, site will also match the front page (remember that its full format name is site-index).
  2. The more specialized a format name that matches our page is, the higher precedence it has when deciding if the block will be allowed. For example, mod, mod-quiz and mod-quiz-view all match the quiz view page. But if all three are present, mod-quiz-view will take precedence over the other two because it is a better match.
  3. The character * can be used in place of any word. For example, mod and mod-* are equivalent. At the time of this document's writing, there is no actual reason to utilize this "wildcard matching" feature, but it exists for future usage.
  4. The order that the format names appear does not make any difference.

 

 

cron

要使block具有task schedule功能,只需要2

Step 1: add function cron

function cron() {
    mtrace( "Hey, my cron script is running" );
    // do something
    return true;
}

 

Step 2: in init() method, setthe (minimum) execution interval for your cron function

        $this->cron = 300; //set 5 minutes interval

 

注意:如果你修改了cron interval,你必须修改blockversion number,然后visit  Notifications page才能生效

 

NOTE: Theblock cron is designed to call the cron script for that block type only. 也就是说cron并不关心该block有多少个instance cronfunction里,你如果需要获取blockinstance,你要自己写代码来iterate over them

例:

function cron() {
    // get the block type from the name
    $blocktype = get_record( 'block', 'name', 'my_block_name' );
    // get the instances of the block
    $instances = get_records( 'block_instance','blockid',$blocktype->id );
    // iterate over the instances
    foreach ($instances as $instance) {
        // recreate block object
        $block = block_instance( 'my_block_name', $instance );
        $someconfigitem = $block->config->item2;
    }
}

 

Lists and Iconstype block

它是一个只提供itemlistblock type,该list的每一行只显示一个item and an optional image (icon) next to the item。典型的例子就是course page里的”administration” block

 

开发这种类型的block,你应该extend block_list,而不是block_base。另外,在getContent里不是使用$this->content->text,而是使用$this->content->items and$this->content->icons array

class block_my_menu extends block_list {

     // The init() method does not need to change at all

function get_content() {

  if ($this->content !== null) {

    return $this->content;

  }

 
  $this->content         = new stdClass;

  $this->content->items  = array();

  $this->content->icons  = array();

  $this->content->footer = 'Footer here...';

 

  $this->content->items[] ='<a href="a.php">Menu Option1</a>';

  $this->content->icons[] = '<imgsrc="1.gif" />';

  // Add morelist items here

  return $this->content;

}

}

 

 

 

 

 

 

原创粉丝点击