Developer Area/Core Subsystems/Blocktypes

From Mahara Wiki
Jump to: navigation, search

Note: with the release of 1.1, the blocktype API has changed slightly, so some parts of this documentation may be out of date.

In a view, a blocktype is one of the blocks that sits in the tabbed panel at the top of the page and can be dragged on to a view. Example blocktypes include "Blog Post", "Profile Information" and "Text Box". Note that this is different from the various sideblocks that are displayed throughout the site.

Some blocktypes, such as blog post, are tied to artefacts. Some, like profile information, are tied to more than one artefact, and others, like text box, are not related to artefacts.

When blocktypes are dragged on to a view, they create a "block instance". Block instances are to block types what objects are to classes in Object Oriented programming. A view can have many different block instances of many different block types - for example, you could add the blog post blocktype to a view twice and have each one show a different blog post.

Mahara has a plugin API for writing new blocktypes for placement in views. This makes adding your own blocktypes an easy job.

Where do I start?

Firstly, you need to work out whether the blocktype you want to write is related to artefacts or not. This is easy - will your blocktype display an artefact or not? And if so, what kind of artefact?

Blocktypes that show artefacts are known as artefact blocktypes, other ones are known as system blocktypes. System blocktypes live in the htdocs/blocktype/ directory, in their own subdirectory. Artefact blocktypes live in htdocs/artefact/[artefactplugin]/blocktype, again in their own subdirectory.

An example blocktype

Let's have a look at the textbox blocktype, as it is as of 2007/11/01. Text boxes are just a WYSIWYG editor for people to put free text in. It lives under the 'general' tab in the views interface. Because no artefacts are involved, it lives in the htdocs/blocktype/textbox directory.

In this directory is ia folder and a few files: lang/, lib.php, thumb.png and version.php.

  • The lang/ directory contains translations for strings used in the blocktype. Currently it has just an english translation.
  • The lib.php file contains the class that implements the blocktype API - it is the meat of the blocktype, and the file we are most interested in.
  • thumb.png is the image that is seen in the tabbed interface for choosing blocktypes. You might want to use an image editor to change it, but make sure it remains the same width and height.
  • version.php contains version information about the blocktype. It's a simple enough file to understand.

lib.php

This is where the action is. It contains a class definition that looks something like this:

class PluginBlocktypeTextbox extends SystemBlocktype {
 
      public static function get_title() {
          return get_string('title', 'blocktype.textbox');
      }
 
      public static function get_description() {
          return get_string('description', 'blocktype.textbox');
      }
 
      public static function get_categories() {
          return array('general');
      }
 
      public static function render_instance(BlockInstance $instance) {
          $configdata = $instance->get('configdata');
          $text = (isset($configdata['text'])) ? $configdata['text'] : '';
          return $text;
      }
 
      public static function has_instance_config() {
          return true;
      }
 
      public static function instance_config_form($instance) {
          $configdata = $instance->get('configdata');
          return array(
              'text' => array(
                  'type' => 'tinywysiwyg',
                  'width' => '90%',
                  'height' => '150px',
                  'defaultvalue' => $configdata['text'],
              ),
          );
      }
 
      public static function instance_config_save($values) {
          global $USER;
          if (!get_account_preference($USER->get('id'), 'wysiwyg')) {
              $values['text'] = format_whitespace($values['text']);
          }
          else {
              $values['text'] = clean_text($values['text']);
          }
          return $values;
      }
 
  }

The PluginBlocktypeTextbox class

This class extends the SystemBlocktype class and implements the required methods to be a valid system blocktype. The class is called PluginTypeTextbox and lives in a directory called 'textbox'. These things are related - if your blocktype is called 'flobber' then it should live in a directory called 'flobber' and the class should be called 'PluginBlocktypeFlobber'.

The static method get_title returns the title for the block, as seen in the tabbed interface for choosing blocktypes. It uses get_string to return a translated value if the user is using another language.

The static method get_description is very similar, returning the description seen below the title in the blocktype choosing interface.

get_categories is a method that returns an array of the categories that the blocktype is in. The textbox claims it is in the 'general' category, and so appears under the general tab. Blocktypes can be in more than one category. The allowed categories names are in the blocktype_category table in the database.

render_instance is where much of the action takes place. This method needs to return the HTML that will be displayed in each blockinstance. In our example, the blockinstance is queried for its configuration data, and the 'text' field of it is used as the HTML.

has_instance_config returns whether this blocktype has a configuration form. Some do not, although most of them will.

instance_config_form returns the elements for the pieform that will be used to configure the blockinstance. In this case, the form contains a 'text' element that is a tiny WYSIWYG editor. Its default value is the text config data for the block - and because the element is called 'text', when the config form is saved then the value of the WYSIWYG editor will be used as the text configuration data for the blockinstance.

instance_config_save is a callback function that allows the blocktype to modify the data submitted before it is saved. In this case, it makes sure that the text is correctly formatted depending on whether the user has the WYSIWYG editor enabled or disabled. Note that the actual saving is handled by the blocktype API itself, so blocktypes do not have to do it themselves.

An Artefact Blocktype

The image blocktype, in the htdocs/artefact/file/blocktype directory, deals with showing images that users have uploaded to their files section. As of 2007/11/01, it looks like this:

class PluginBlocktypeImage extends PluginBlocktype {
 
      public static function get_title() {
          return get_string('title', 'blocktype.file/image');
      }
 
      public static function get_description() {
          return get_string('description', 'blocktype.file/image');
      }
 
      public static function get_categories() {
          return array('file', 'images');
      }
 
      public static function render_instance(BlockInstance $instance) {
          require_once(get_config('docroot') . 'lib/artefact.php');
          $configdata = $instance->get('configdata'); // this will make sure to unserialize it for us
          $configdata['viewid'] = $instance->get('view');
 
          // This can be either an image or profileicon. They both implement
          // render_self
          $result = '';
          if (isset($configdata['artefactid'])) {
              $image = artefact_instance_from_id($configdata['artefactid']);
 
              if ($image instanceof ArtefactTypeProfileIcon) {
                  $src = get_config('wwwroot') . 'thumb.php?type=profileiconbyid&id='
                      . $configdata['artefactid'];
                  $description = $image->get('title');
              }
              else {
                  $src = get_config('wwwroot') . 'artefact/file/download.php?file='
                      . $configdata['artefactid'];
                  $src .= '&view=' . $instance->get('view');
                  $description = $image->get('description');
              }
 
              if (!empty($configdata['width'])) {
                  $src .= '&maxwidth=' . $configdata['width'];
              }
 
              $result  = '</nowiki><div class=""center""><div>';
              $result .= '[[%22'|[[Image:%22'|"']]]]';
              $result .= '</div>';
 
              $description = (is_a($image, 'ArtefacttypeImage'))
                 ? $image->get('description')
                 : $image->get('title');
              if (!empty($configdata['showdescription']) && $description) {
                  $result .= '' . $description . '';
              }
              $result .= '</div><nowiki>';
          }
 
          return $result;
      }
 
      public static function get_artefacts(BlockInstance $instance) {
          $configdata = $instance->get('configdata');
          if (isset($configdata['artefactid'])) {
              return array($configdata['artefactid']);
          }
          return false;
      }
 
      public static function has_instance_config() {
          return true;
      }
 
      public static function instance_config_form($instance) {
          $configdata = $instance->get('configdata');
          return array(
              self::artefactchooser_element((isset($configdata['artefactid']))
                  ? $configdata['artefactid'] : null),
              'showdescription' => array(
                  'type'  => 'checkbox',
                  'title' => get_string('showdescription', 'blocktype.file/image'),
                  'defaultvalue' => $configdata['showdescription'],
              ),
              'width' => array(
                  'type' => 'text',
                  'title' => get_string('width', 'blocktype.file/image'),
                  'size' => 3,
                  'description' => get_string('widthdescription', 'blocktype.file/image'),
                  'rules' => array(
                      'minvalue' => 16,
                      'maxvalue' => get_config('imagemaxwidth'),
                  ),
                  'defaultvalue' => (isset($configdata['width'])) ? $configdata['width'] : '',
              ),
          );
      }
 
      public static function artefactchooser_element($default=null) {
          return array(
              'name'  => 'artefactid',
              'type'  => 'artefactchooser',
              'title' => get_string('image'),
              'defaultvalue' => $default,
              'rules' => array(
                  'required' => true,
              ),
              'blocktype' => 'image',
              'limit' => 5,
              'artefacttypes' => array('image', 'profileicon'),
              'template' => 'artefact:file:artefactchooser-element.tpl',
          );
      }
 
      /**
       * Optional method. If specified, allows the blocktype class to munge the
       * artefactchooser element data before it's templated
       *
       * Note: this method is the same as the one for the 'filedownload' blocktype
       */
      public static function artefactchooser_get_element_data($artefact) {
          $artefact->icon = call_static_method(generate_artefact_class_name(
              $artefact->artefacttype), 'get_icon', array('id' => $artefact->id));
          if ($artefact->artefacttype == 'profileicon') {
              $artefact->hovertitle  =  $artefact->note;
              if ($artefact->title) {
                  $artefact->hovertitle .= ': ' . $artefact->title;
              }
          }
          else {
              $artefact->hovertitle  =  $artefact->title;
              if ($artefact->description) {
                  $artefact->hovertitle .= ': ' . $artefact->description;
              }
          }
          $artefact->title       = str_shorten($artefact->title, 20);
          $artefact->description = ($artefact->artefacttype == 'profileicon')
              ? $artefact->title
              : $artefact->description;
 
          return $artefact;
      }
 
  }

This blocktype is more complicated than the textbox. The differences are outlined below:

The get_title and get_description methods are similar to their textbox counterparts, however they use a namespaced section name - blocktype.file/image. The first part of the section is always "blocktype", then after the period is "artefactplugin/blocktype". You will need to create this lang file if it is not yet present within your artefactname/blocktype/bloctypename/lang/en.utf8/ directory as blocktype.blocktypename.php.

The get_artefacts method is required for artefact blocktypes. It returns the IDs of any artefacts in the blocktype, which are used by Mahara to assert whether an artefact is in a given view.

The instance_config_form includes a call to self::artefactchooser_element. This method returns an artefactchooser pieform element, which is used to select which artefact or artefacts should be in the blockinstance. This is the element that allows pagination through the artefacts available.