[Svn] r2266 - in branches/devel-api: . plugins plugins/markasjunk program/include skins/default/templates

trac at roundcube.net trac at roundcube.net
Sun Feb 1 16:27:12 CET 2009


Author: thomasb
Date: 2009-02-01 09:27:12 -0600 (Sun, 01 Feb 2009)
New Revision: 2266

Added:
   branches/devel-api/plugins/markasjunk/
   branches/devel-api/plugins/markasjunk/junk_act.png
   branches/devel-api/plugins/markasjunk/junk_pas.png
   branches/devel-api/plugins/markasjunk/markasjunk.js
   branches/devel-api/plugins/markasjunk/markasjunk.php
Modified:
   branches/devel-api/index.php
   branches/devel-api/program/include/html.php
   branches/devel-api/program/include/rcmail.php
   branches/devel-api/program/include/rcube_plugin.php
   branches/devel-api/program/include/rcube_plugin_api.php
   branches/devel-api/skins/default/templates/message.html
Log:
Allow plugins to add client scripts and handle HTTP requests

Modified: branches/devel-api/index.php
===================================================================
--- branches/devel-api/index.php	2009-01-31 14:55:38 UTC (rev 2265)
+++ branches/devel-api/index.php	2009-02-01 15:27:12 UTC (rev 2266)
@@ -2,7 +2,7 @@
 /*
  +-------------------------------------------------------------------------+
  | RoundCube Webmail IMAP Client                                           |
- | Version 0.2-20090101                                                    |
+ | Version 0.3-20090201                                                    |
  |                                                                         |
  | Copyright (C) 2005-2009, RoundCube Dev. - Switzerland                   |
  |                                                                         |
@@ -213,9 +213,14 @@
 while ($redirects < 5) {
   $stepfile = !empty($action_map[$RCMAIL->task][$RCMAIL->action]) ?
     $action_map[$RCMAIL->task][$RCMAIL->action] : strtr($RCMAIL->action, '-', '_') . '.inc';
-    
+
+  // execute a plugin action
+  if (eregi('^plugin.', $RCMAIL->action)) {
+    $RCMAIL->plugins->exec_action($RCMAIL->action);
+    break;
+  }
   // try to include the step file
-  if (is_file(($incfile = 'program/steps/'.$RCMAIL->task.'/'.$stepfile))) {
+  else if (is_file(($incfile = 'program/steps/'.$RCMAIL->task.'/'.$stepfile))) {
     include($incfile);
     $redirects++;
   }

Added: branches/devel-api/plugins/markasjunk/junk_act.png
===================================================================
(Binary files differ)


Property changes on: branches/devel-api/plugins/markasjunk/junk_act.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/devel-api/plugins/markasjunk/junk_pas.png
===================================================================
(Binary files differ)


Property changes on: branches/devel-api/plugins/markasjunk/junk_pas.png
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream

Added: branches/devel-api/plugins/markasjunk/markasjunk.js
===================================================================
--- branches/devel-api/plugins/markasjunk/markasjunk.js	                        (rev 0)
+++ branches/devel-api/plugins/markasjunk/markasjunk.js	2009-02-01 15:27:12 UTC (rev 2266)
@@ -0,0 +1,34 @@
+/* Mark-as-Junk plugin script */
+
+function rcmail_markasjunk(prop)
+{
+  if (!rcmail.env.uid && (!rcmail.message_list || !rcmail.message_list.get_selection().length))
+    return;
+  
+    var uids = rcmail.env.uid ? rcmail.env.uid : rcmail.message_list.get_selection().join(',');
+    
+    rcmail.set_busy(true, 'loading');
+    rcmail.http_post('plugin.markasjunk', '_uid='+uids+'&_mbox='+urlencode(rcmail.env.mailbox), true);
+}
+
+// callback for app-onload event
+if (window.rcmail) {
+  rcmail.add_onload(function(){
+    
+    // create button
+    var button = $('<A>').html('<img src="plugins/markasjunk/junk_pas.png" id="rcmButtonMarkAsJunk" width="32" height="32" alt="" title="" />').css('cursor', 'pointer');
+    button.bind('click', function(e){ return rcmail.command('plugin.markasjunk', this) });
+    
+    // add and register
+    rcmail.add_element(button, 'toolbar');
+    rcmail.register_button('plugin.markasjunk', 'rcmButtonMarkAsJunk', 'image', 'plugins/markasjunk/junk_act.png');
+    rcmail.register_command('plugin.markasjunk', rcmail_markasjunk, rcmail.env.uid);
+    
+    // add event-listener to message list
+    if (rcmail.message_list)
+      rcmail.message_list.addEventListener('select', function(list){
+        rcmail.enable_command('plugin.markasjunk', list.get_selection().length > 0);
+      });
+  })
+}
+

Added: branches/devel-api/plugins/markasjunk/markasjunk.php
===================================================================
--- branches/devel-api/plugins/markasjunk/markasjunk.php	                        (rev 0)
+++ branches/devel-api/plugins/markasjunk/markasjunk.php	2009-02-01 15:27:12 UTC (rev 2266)
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * Sample plugin that adds a new button to the mailbox toolbar
+ * to mark the selected messages as Junk and move them to the Junk folder
+ */
+class markasjunk extends rcube_plugin
+{
+
+  function init()
+  {
+    $this->task = 'mail';
+    
+    $this->register_action('plugin.markasjunk', array($this, 'request_action'));
+    $GLOBALS['IMAP_FLAGS']['JUNK'] = 'Junk';
+    
+    $rcmail = rcmail::get_instance();
+    if ($rcmail->action == '' || $rcmail->action == 'show')
+      $this->include_script('markasjunk.js');
+  }
+
+  function request_action()
+  {
+    $rcmail = rcmail::get_instance();
+    
+    $count = sizeof(explode(',', ($uids = get_input_value('_uid', RCUBE_INPUT_POST))));
+    $mbox = get_input_value('_mbox', RCUBE_INPUT_POST);
+    
+    $rcmail->imap->set_flag($uids, 'JUNK');
+    
+    if (($junk_mbox = $rcmail->config->get('junk_mbox')) && $mbox != $junk_mbox) {
+      $rcmail->output->command('move_messages', $junk_mbox);
+    }
+    
+    $rcmail->output->show_message('reportedasspam', 'confirmation');
+    $rcmail->output->send();
+  }
+
+}
\ No newline at end of file

Modified: branches/devel-api/program/include/html.php
===================================================================
--- branches/devel-api/program/include/html.php	2009-01-31 14:55:38 UTC (rev 2265)
+++ branches/devel-api/program/include/html.php	2009-02-01 15:27:12 UTC (rev 2266)
@@ -33,7 +33,7 @@
     protected $content;
 
     public static $common_attrib = array('id','class','style','title','align');
-    public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','tr','th','td','style');
+    public static $containers = array('iframe','div','span','p','h1','h2','h3','form','textarea','table','tr','th','td','style','script');
     public static $lc_tags = true;
 
     /**

Modified: branches/devel-api/program/include/rcmail.php
===================================================================
--- branches/devel-api/program/include/rcmail.php	2009-01-31 14:55:38 UTC (rev 2265)
+++ branches/devel-api/program/include/rcmail.php	2009-02-01 15:27:12 UTC (rev 2266)
@@ -319,6 +319,9 @@
     // add some basic label to client
     $this->output->add_label('loading');
     
+    // load plugins stuff
+    $this->plugins->init_gui($this->output);
+    
     return $this->output;
   }
   

Modified: branches/devel-api/program/include/rcube_plugin.php
===================================================================
--- branches/devel-api/program/include/rcube_plugin.php	2009-01-31 14:55:38 UTC (rev 2265)
+++ branches/devel-api/program/include/rcube_plugin.php	2009-02-01 15:27:12 UTC (rev 2266)
@@ -5,7 +5,7 @@
  | program/include/rcube_plugin.php                                      |
  |                                                                       |
  | This file is part of the RoundCube Webmail client                     |
- | Copyright (C) 2008, RoundCube Dev. - Switzerland                      |
+ | Copyright (C) 2008-2009, RoundCube Dev. - Switzerland                 |
  | Licensed under the GNU GPL                                            |
  |                                                                       |
  | PURPOSE:                                                              |
@@ -26,22 +26,81 @@
  */
 abstract class rcube_plugin
 {
+  public $ID;
   public $api;
   public $task;
+  protected $home;
 
+  /**
+   * Default constructor.
+   */
   public function __construct($api)
   {
+    $this->ID = get_class($this);
     $this->api = $api;
+    $this->home = $api->dir . DIRECTORY_SEPARATOR . $this->ID;
+
     $this->init();
   }
   
+  /**
+   * Initialization method, needs to be implemented by the plugin itself
+   */
   abstract function init();
 
+  /**
+   * Register a callback function for a specific (server-side) hook
+   *
+   * @param string Hook name
+   * @param mixed Callback function as string or array with object reference and method name
+   */
   public function add_hook($hook, $callback)
   {
     $this->api->register_hook($hook, $callback);
   }
 
+  /**
+    * Register a handler for a specific client-request action
+    *
+    * The callback will be executed upon a request like /?_task=plugin&_action=myaction
+    *
+    * @param string Action name (should be unique)
+    * @param mixed Callback function as string or array with object reference and method name
+   */
+  public function register_action($action, $callback)
+  {
+    $this->api->register_action($action, $this->ID, $callback);
+  }
 
+  /**
+   * Register a handler function for a template object
+   *
+   * When parsing a template for display, tags like <roundcube:object name="plugin.myobject" />
+   * will be replaced by the return value if the registered callback function.
+   *
+   * @param string Object name (should be unique and start with 'plugin.')
+   * @param mixed Callback function as string or array with object reference and method name
+   */
+  public function register_handler($name, $callback)
+  {
+    $this->api->register_handler($name, $this->ID, $callback);
+  }
+
+  /**
+   * Make this javascipt file available on the client
+   *
+   * @param string File path; absolute or relative to the plugin directory
+   */
+  public function include_script($fn)
+  {
+    // relative file name
+    if ($fn[0] != '/' && !eregi('^https?://', $fn)) {
+      $fn = $this->ID.'/'.$fn;
+    }
+    
+    $this->api->include_script($fn);
+  }
+
+
 }
 

Modified: branches/devel-api/program/include/rcube_plugin_api.php
===================================================================
--- branches/devel-api/program/include/rcube_plugin_api.php	2009-01-31 14:55:38 UTC (rev 2265)
+++ branches/devel-api/program/include/rcube_plugin_api.php	2009-02-01 15:27:12 UTC (rev 2266)
@@ -28,10 +28,18 @@
 {
   static private $instance;
   
+  public $dir;
+  public $url = 'plugins/';
+  
   private $handlers = array();
   private $plugins = array();
+  private $actions = array();
+  private $actionmap = array();
+  private $templobjects = array();
+  private $objectsmap = array();
+  private $scripts = array();
+  
 
-
   /**
    * This implements the 'singleton' design pattern
    *
@@ -58,8 +66,10 @@
     if (!$rcmail->config->get('devel_mode'))
       return;
     
+    $this->dir = realpath($rcmail->config->get('plugins_dir'));
+    
     // load all enabled plugins
-    $plugins_dir = dir($rcmail->config->get('plugins_dir'));
+    $plugins_dir = dir($this->dir);
     $plugins_enabled = (array)$rcmail->config->get('plugins', array());
     
     foreach ($plugins_enabled as $plugin_name) {
@@ -73,15 +83,16 @@
           $plugin = new $plugin_name($this);
           // check inheritance and task specification
           if (is_subclass_of($plugin, 'rcube_plugin') && (!$plugin->task || $plugin->task == $rcmail->task)) {
+            $plugin->ID = $plugin_name;
             $this->plugins[] = $plugin;
           }
         }
         else {
-          trigger_error(array('code' => 520, 'type' => 'php', 'message' => "No plugin class $plugin_name found in $fn"), true, false);
+          raise_error(array('code' => 520, 'type' => 'php', 'message' => "No plugin class $plugin_name found in $fn"), true, false);
         }
       }
       else {
-        trigger_error(array('code' => 520, 'type' => 'php', 'message' => "Failed to load plugin file $fn"), true, false);
+        raise_error(array('code' => 520, 'type' => 'php', 'message' => "Failed to load plugin file $fn"), true, false);
       }
     }
     
@@ -90,6 +101,20 @@
   
   
   /**
+   * Add GUI things once the output objects is created
+   */
+  public function init_gui($output)
+  {
+    if ($output->type == 'html') {
+      $output->add_handlers($this->objectsmap);
+      
+      foreach ($this->scripts as $script)
+        $output->add_header(html::tag('script', array('type' => "text/javascript", 'src' => $script)));
+    }
+  }
+  
+  
+  /**
    * Allows a plugin object to register a callback for a certain hook
    *
    * @param string Hook name
@@ -100,7 +125,7 @@
     if (is_callable($callback))
       $this->handlers[$hook][] = $callback;
     else
-      trigger_error(array('code' => 521, 'type' => 'php', 'message' => "Invalid callback function for $hook"), true, false);
+      raise_error(array('code' => 521, 'type' => 'php', 'message' => "Invalid callback function for $hook"), true, false);
   }
   
   
@@ -127,7 +152,76 @@
     
     return $args;
   }
+
+
+  /**
+   * Let a plugin register a handler for a specific request
+   *
+   * @param string Action name (_task=plugin&_action=...)
+   * @param string Plugin name that registers this action
+   * @param mixed Callback: string with global function name or array($obj, 'methodname')
+   */
+  public function register_action($action, $owner, $callback)
+  {
+    // can register action only if it's not taken or registered by myself
+    if (!isset($this->actionmap[$action]) || $this->actionmap[$action] == $owner) {
+      $this->actions[$action] = $callback;
+      $this->actionmap[$action] = $owner;
+    }
+    else {
+      raise_error(array('code' => 523, 'type' => 'php', 'message' => "Cannot register action $action; already taken by another plugin"), true, false);
+    }
+  }
+
+
+  /**
+   * This method handles requests like _task=plugin&_action=foo
+   * It executes the callback function that was registered with the given action.
+   *
+   * @param string Action name
+   */
+  public function exec_action($action)
+  {
+    if (isset($this->actions[$action])) {
+      call_user_func($this->actions[$action]);
+    }
+    else {
+      raise_error(array('code' => 524, 'type' => 'php', 'message' => "No