Index: tinymce.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/tinymce/tinymce.module,v
retrieving revision 1.90.4.23
diff -u -p -r1.90.4.23 tinymce.module
--- tinymce.module	6 May 2007 01:41:35 -0000	1.90.4.23
+++ tinymce.module	30 Sep 2007 20:22:08 -0000
@@ -221,9 +221,15 @@ EOD;
       // We have to do this becuase of some unfocused CSS in certain themes. See http://drupal.org/node/18879 for details
       drupal_set_html_head('<style type="text/css" media="all">.mceEditor img { display: inline; }</style>');
     }
-    // Load a TinyMCE init for each textarea.
-    if ($init) drupal_add_js($tinymce_invoke, 'inline');
+    
+    if ($init) {
+      // Load a TinyMCE init for each textarea.
+      drupal_add_js($tinymce_invoke, 'inline');
 
+      // Load external plugins.
+      tinymce_load_plugins();
+    }
+    
     //settings are saved as strings, not booleans
     if ($profile->settings['show_toggle'] == 'true') {
       // Make sure to append to #suffix so it isn't completely overwritten
@@ -237,6 +243,29 @@ EOD;
 }
 
 /**
+ * Load external plugins.
+ * 
+ * Note: tinyMCE.loadPlugin() need not be invoked more than once.
+ */
+function tinymce_load_plugins() {
+  static $plugins_added;
+  
+  if (!isset($plugins_added)) {
+    $plugins_added = TRUE;
+    
+    $init_plugins = '';
+    $plugins = module_invoke_all('wysiwyg_plugin', 'tinymce');
+    foreach ($plugins as $name => $plugin) {
+      $name = _tinymce_plugin_name('remove', $name);
+      $init_plugins .= "tinyMCE.loadPlugin('$name', '". base_path() . $plugin['path'] ."');\n";
+    }
+    if (!empty($init_plugins)) {
+      drupal_add_js($init_plugins, 'inline');
+    }
+  }
+}
+
+/**
  * Implementation of hook_user().
  */
 function tinymce_user($type, &$edit, &$user, $category = NULL) {
@@ -387,11 +416,22 @@ function _tinymce_get_buttons($skip_meta
   include_once(drupal_get_path('module', 'tinymce'). '/plugin_reg.php');
   $plugins = _tinymce_plugins();
 
+  // Allow Drupal contrib modules to add external plugins.
+  $plugins = array_merge($plugins, module_invoke_all('wysiwyg_plugin', 'tinymce'));
+
   if ($skip_metadata == FALSE && is_array($plugins)) {
     foreach ($plugins as $name => $plugin) {
-      $file = drupal_get_path('module', 'tinymce'). '/tinymce/jscripts/tiny_mce/plugins/'. $name .'/editor_plugin_src.js';
+      if (!isset($plugin['path'])) {
+        $file = drupal_get_path('module', 'tinymce'). '/tinymce/jscripts/tiny_mce/plugins/'. $name .'/editor_plugin_src.js';
+      }
+      else {
+        $file = $plugin['path'] .'/editor_plugin_src.js';
+      }
       // Grab the plugin metadata by scanning the *.js file.
       if (file_exists($file)) {
+        if (isset($plugin['path'])) {
+          $metadata[$name]['path'] = $plugin['path'];
+        }
         $lines = file($file);
         $has_longname = FALSE;
         $has_infourl = FALSE;
@@ -424,6 +464,30 @@ function _tinymce_get_buttons($skip_meta
 
   return $plugins;
 }
+
+/**
+ * Add or remove leading hiven to/of (external) plugin names.
+ * 
+ * @param string $op
+ *   Operation to perform, 'add' or 'remove'.
+ * @param string $name
+ *   A plugin name.
+ */
+function _tinymce_plugin_name($op, $name) {
+  if ($op == 'add') {
+    if (strpos($name, '-') !== 0) {
+      return '-'. $name;
+    }
+    return $name;
+  }
+  elseif ($op == 'remove') {
+    if (strpos($name, '-') === 0) {
+      return substr($name, 1);
+    }
+    return $name;
+  }
+}
+
 /********************************************************************
  * Module Functions :: Public
  ********************************************************************/
@@ -543,11 +607,13 @@ function tinymce_config($profile) {
       $plugin_tracker = array();
       foreach ($plugins as $rname => $rplugin) { // Plugin name
         foreach ($rplugin as $mce_key => $mce_value) { // TinyMCE key
-          foreach ($mce_value as $k => $v) { // Buttons
-            if ($settings['buttons'][$rname . '-' . $v]) {
-              // Font isn't a true plugin, rather it's buttons made available by the advanced theme
-              if (!in_array($rname, $plugin_tracker) && $rname != 'font') $plugin_tracker[] = $rname;
-              $init[$mce_key][] = $v;
+          if (is_array($mce_value)) {
+            foreach ($mce_value as $k => $v) { // Buttons
+              if ($settings['buttons'][$rname . '-' . $v]) {
+                // Font isn't a true plugin, rather it's buttons made available by the advanced theme
+                if (!in_array($rname, $plugin_tracker) && $rname != 'font') $plugin_tracker[] = $rname;
+                $init[$mce_key][] = $v;
+              }
             }
           }
         }
@@ -560,14 +626,21 @@ function tinymce_config($profile) {
       // Add the rest of the TinyMCE config options to the $init array for each button.
       if (is_array($plugin_tracker)) {
         foreach ($plugin_tracker as $pname) {
-          if ($pname != 'default') $init['plugins'][] = $pname;
+          if ($pname != 'default') {
+            if (!isset($plugins[$pname]['path'])) {
+              $init['plugins'][] = $pname;
+            }
+            else {
+              $init['plugins'][] = _tinymce_plugin_name('add', $pname);
+            }
+          }
           foreach ($plugins[$pname] as $mce_key => $mce_value) {
             // Don't overwrite buttons or extended_valid_elements
             if ($mce_key == 'extended_valid_elements') {
               // $mce_value is an array for extended_valid_elements so just grab the first element in the array (never more than one)
               $init[$mce_key][] = $mce_value[0];
             }
-            else if (!strstr($mce_key, 'theme_advanced_buttons')) {
+            else if ($mce_key != 'path' && !strstr($mce_key, 'theme_advanced_buttons')) {
               $init[$mce_key] = $mce_value;
             }
           }
@@ -802,7 +875,12 @@ function tinymce_profile_form_build($edi
     if (is_array($meta['buttons'])) {
       foreach ($meta['buttons'] as $button) {
         if ($name != 'default') {
-          $img_src = drupal_get_path('module', 'tinymce'). "/tinymce/jscripts/tiny_mce/plugins/$name/images/$name.gif";
+          if (!isset($meta['path'])) {
+            $img_src = drupal_get_path('module', 'tinymce'). "/tinymce/jscripts/tiny_mce/plugins/$name/images/$name.gif";
+          }
+          else {
+            $img_src = $meta['path'] ."/images/$name.gif";
+          }
 
           //correct for plugins that have more than one button
           if (!file_exists($img_src)) {
@@ -1066,6 +1144,11 @@ function tinymce_profile_overview() {
 function tinymce_profile_save($edit) {
   db_query("DELETE FROM {tinymce_settings} WHERE name = '%s' or name = '%s'", $edit['name'], $edit['old_name']);
   db_query("DELETE FROM {tinymce_role} WHERE name = '%s' or name = '%s'", $edit['name'], $edit['old_name']);
+  
+  // Remove FAPI values.
+  // @see system_settings_form_submit()
+  unset($edit['submit'], $edit['reset'], $edit['form_id'], $edit['op'], $edit['form_token']);
+  
   db_query("INSERT INTO {tinymce_settings} (name, settings) VALUES ('%s', '%s')", $edit['name'], serialize($edit));
   foreach ($edit['rids'] as $rid => $value) {
     db_query("INSERT INTO {tinymce_role} (name, rid) VALUES ('%s', %d)", $edit['name'], $rid);
