Index: inline.module =================================================================== RCS file: /cvs/drupal-contrib/contributions/modules/inline/inline.module,v retrieving revision 1.35 diff -u -p -r1.35 inline.module --- inline.module 28 Jan 2009 21:43:05 -0000 1.35 +++ inline.module 1 Feb 2009 10:35:10 -0000 @@ -14,6 +14,31 @@ function inline_perm() { } /** + * Implementation of hook_inline_info(). + * + * @return + * An associative array of inline tags provided by this module, keyed by tag + * name with the following properties: + * - callback: (optional) The inline callback function name. Defaults to + * MODULENAME_TAGNAME_inline(). + * - file: (optional) The include file where callback function resides. + * - path: (optional) The path of the include file. Defaults to the module's + * directory. + */ +function inline_inline_info() { + $info = array(); + $info['node'] = array( + 'file' => 'inline.node.inc', + ); + if (module_exists('upload')) { + $info['upload'] = array( + 'file' => 'inline.upload.inc', + ); + } + return $info; +} + +/** * @defgroup inline_macro Inline input filter/macro parsing * @{ */ @@ -53,16 +78,48 @@ function inline_filter($op, $delta = 0, } /** + * Invoke a hook_inline() implementation. + */ +function inline_invoke($params, $op) { + if (!function_exists($params['callback'])) { + if (isset($params['file'])) { + if (!isset($params['path'])) { + $params['path'] = drupal_get_path('module', $params['module']); + } + require_once $params['path'] . '/' . $params['file']; + } + } + if (function_exists($params['callback'])) { + $args = func_get_args(); + array_shift($args); + return call_user_func_array($params['callback'], $args); + } +} + +/** * Return all inline macros as an array. */ function inline_get_macros($text) { $macros = array(); // Collect possible inline tag names. - // @todo Use module_invoke_all() to allow more than one Inline implementation - // per module, and allow to configure which Inline implementations should - // be enabled per filter format. - $tagnames = '('. implode('|', module_implements('inline')) .')'; + $implementations = array(); + foreach (module_implements('inline_info') as $module) { + $module_tags = module_invoke($module, 'inline_info'); + if (!is_array($module_tags)) { + continue; + } + foreach ($module_tags as $tag => $info) { + $module_tags[$tag]['module'] = $module; + $module_tags[$tag]['tag'] = $tag; + if (!isset($info['callback'])) { + $module_tags[$tag]['callback'] = $module . '_' . $tag . '_inline'; + } + } + $implementations = array_merge($implementations, $module_tags); + } + + $tagnames = '('. implode('|', array_keys($implementations)) .')'; // @todo Add support for escaped [, ] chars preg_match_all('@\['. $tagnames .'\s*\|([^\[\]]+)\]@', $text, $matches); // Don't process duplicates. @@ -71,11 +128,10 @@ function inline_get_macros($text) { foreach ($tags as $n => $macro) { // @todo Add support for escaped | char $macro_params = array_map('trim', explode('|', $macro)); - $params = array(); - $params['module'] = $matches[1][$n]; + $params = $implementations[$matches[1][$n]]; // @todo Add a macro counter for each found tag *per module* to allow stuff // like odd/even classes (f.e. $params['#count']). - $args = module_invoke($params['module'], 'inline', 'args'); + $args = inline_invoke($params, 'args'); foreach ($macro_params as $param) { list($key, $value) = explode('=', $param, 2); $key = trim($key); @@ -95,14 +151,14 @@ function inline_get_macros($text) { $value = (bool)$value; } // Stack multiple occurences. - if (isset($params[$key]) && isset($args[$key]['#multiple']) && $args[$key]['#multiple']) { - if (!is_array($params[$key])) { - $params[$key] = array($params[$key]); + if (isset($params['params'][$key]) && isset($args[$key]['#multiple']) && $args[$key]['#multiple']) { + if (!is_array($params['params'][$key])) { + $params['params'][$key] = array($params['params'][$key]); } - $params[$key][] = $value; + $params['params'][$key][] = $value; } else { - $params[$key] = $value; + $params['params'][$key] = $value; } } // The full unaltered tag is the key for the filter attributes array. @@ -122,28 +178,22 @@ function inline_get_macros($text) { * Whether the supplied input is valid (TRUE) or not (FALSE). */ function inline_validate_params($params) { - // Return if no module has been supplied or module does not implement - // hook_inline(). - if (!isset($params['module']) || !module_hook($params['module'], 'inline')) { - return FALSE; - } - // Perform basic validation of tag arguments. - $args = module_invoke($params['module'], 'inline', 'args'); + $args = inline_invoke($params, 'args'); foreach ($args as $arg => $info) { // Check if required arguments are set. - if (isset($info['#required']) && $info['#required'] && !isset($info['#default_value']) && (!isset($params[$arg]) || $params[$arg] === '')) { + if (isset($info['#required']) && $info['#required'] && !isset($info['#default_value']) && (!isset($params['params'][$arg]) || $params['params'][$arg] === '')) { return t('Missing argument %arg.', array('%arg' => $arg)); } - if (isset($params[$arg])) { + if (isset($params['params'][$arg])) { // Keep only the first value if multiple flag is not set. - if ((!isset($info['#multiple']) || !$info['#multiple']) && is_array($params[$arg])) { - $params[$arg] = $params[$arg][0]; + if ((!isset($info['#multiple']) || !$info['#multiple']) && is_array($params['params'][$arg])) { + $params['params'][$arg] = $params['params'][$arg][0]; } // Check if supplied arguments are of an expected type. if (isset($info['#type'])) { $typecheck = 'is_'. $info['#type']; - if (!function_exists($typecheck) || !$typecheck($params[$arg])) { + if (!function_exists($typecheck) || !$typecheck($params['params'][$arg])) { return t('Wrong value type supplied for argument %arg.', array('%arg' => $arg)); } } @@ -151,7 +201,7 @@ function inline_validate_params($params) } // Extended validation check by given module. - $module_validation = module_invoke($params['module'], 'inline', 'validate', $params); + $module_validation = inline_invoke($params, 'validate', $params['params']); if (isset($module_validation) && !$module_validation) { return FALSE; } @@ -170,22 +220,22 @@ function inline_validate_params($params) */ function inline_render($params) { // Merge in default values. - $args = module_invoke($params['module'], 'inline', 'args'); + $args = inline_invoke($params, 'args'); foreach ($args as $arg => $info) { - if (empty($params[$arg])) { + if (empty($params['params'][$arg])) { // @todo Allow special defaults like 'current user' or 'current node'. - $params[$arg] = $info['#default_value']; + $params['params'][$arg] = $info['#default_value']; } } // Allow module to prepare tag parameters. - $params = module_invoke($params['module'], 'inline', 'prepare', $params); + $params['params'] = inline_invoke($params, 'prepare', $params['params']); // @todo Prepare operation may not be successful; final validation before // rendering required. // Generate a rendered representation for tag replacement. - $output = module_invoke($params['module'], 'inline', 'render', $params); + $output = inline_invoke($params, 'render', $params['params']); // If an error occured during rendering, we expect the result to be FALSE. if (!is_bool($output)) { @@ -266,8 +316,8 @@ function _inline_alter_macros(&$node, $f // @todo Assign the currently processed field name to $params['#field'] to // allow certain hook_inline implementations to add/ensure this // information in their macros. - if (isset($params['nid']) && $params['nid'] == 0) { - $params['nid'] = $node->nid; + if (isset($params['params']['nid']) && $params['params']['nid'] == 0) { + $params['params']['nid'] = $node->nid; $content = str_replace($macro, inline_build_macro($params), $content); // @todo Update of reference table perhaps needed. } @@ -286,13 +336,10 @@ function _inline_alter_macros(&$node, $f function inline_build_macro($params) { $macro_params = array(); - if (isset($params['module'])) { - $macro_params[] = $params['module']; - unset($params['module']); - } + $macro_params[] = $params['tag']; // @todo Support for #multiple values. // @todo Escape |, [, ] chars in values. - foreach ($params as $key => $value) { + foreach ($params['params'] as $key => $value) { $macro_params[] = $key .'='. $value; } return '['. implode('|', $macro_params) .']'; @@ -437,3 +484,13 @@ function inline_filter_tips($delta, $for * @} End of "defgroup inline_help". */ +/** + * Implementation of hook_wysiwyg_include_directory(). + */ +function inline_wysiwyg_include_directory($type) { + switch ($type) { + case 'plugins': + return $type; + } +} + Index: inline.node.inc =================================================================== RCS file: inline.node.inc diff -N inline.node.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ inline.node.inc 27 Jan 2009 00:56:51 -0000 @@ -0,0 +1,55 @@ + array( + '#title' => t('Node id'), + '#description' => t('A node id to embed.'), + '#type' => 'int', + '#default_value' => 0, + ), + ); + return $args; + + case 'validate': + // Custom validation of user supplied values. + return TRUE; + + case 'prepare': + // Load a node object if valid nid is given. + if ($params['nid'] > 0) { + $node = node_load($params['nid']); + if (node_access('view', $node)) { + $params['#node'] = $node; + } + } + return $params; + + case 'render': + // Return a rendered representation to replace a tag. + if (!isset($params['#node'])) { + return; + } + $node = node_build_content($params['#node'], FALSE, TRUE); + $output = drupal_render($node->content); + return $output; + } +} + Index: plugins/dialog.js =================================================================== RCS file: plugins/dialog.js diff -N plugins/dialog.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/dialog.js 27 Jan 2009 00:19:15 -0000 @@ -0,0 +1,19 @@ +// $Id$ + +Drupal.behaviors.inlineNodeDialog = function (context) { + var target = window.opener || window.parent; + $('form input[type=submit]', context).click(function () { + var settings = {}; + settings.nid = this.form.nid.value; + // Insert into currently attached editor. + target.Drupal.wysiwyg.instances[Drupal.settings.instance].insert(target.Drupal.wysiwyg.plugins.inline_node.insert(settings)); + // Close this dialog. + target.Drupal.wysiwyg.instances[Drupal.settings.instance].closeDialog(window); + return false; + }); + $('a.form-cancel', context).click(function () { + // Close this dialog. + target.Drupal.wysiwyg.instances[Drupal.settings.instance].closeDialog(window); + return false; + }); +} Index: plugins/node.inc =================================================================== RCS file: plugins/node.inc diff -N plugins/node.inc --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/node.inc 27 Jan 2009 00:20:08 -0000 @@ -0,0 +1,58 @@ + t('Inline node'), + 'vendor url' => 'http://drupal.org/project/inline', + 'icon file' => 'inline_node.gif', + 'icon title' => t('Insert or update an embedded content'), + 'settings' => array( + 'dialog' => array( + 'url' => base_path() . 'index.php?q=wysiwyg/inline_node', + 'width' => 700, + 'height' => 500, + ), + ), + // @todo Use a more granular way to validate contents for input formats. + #'extended_valid_elements' => array('img[class|src|border=0|alt|title|width|height|align|name|style]'), + ); + return $plugins; +} + +function inline_node_wysiwyg_dialog($instance) { + drupal_set_title(t('Add or edit external content')); + + // Add dialog JavaScript. + drupal_add_js(drupal_get_path('module', 'inline') . '/plugins/dialog.js'); + + $output = drupal_get_form('inline_node_wysiwyg_dialog_form', $instance); + return $output; +} + +function inline_node_wysiwyg_dialog_form(&$form_state, $instance) { + $form['nid'] = array( + '#type' => 'select', + '#title' => t('Content to embed'), + '#options' => array( + 1 => 'foo bar', + ), + '#description' => t('The selected content will be inserted and embedded into your content.'), + ); + $form['submit'] = array( + '#prefix' => '
', + '#suffix' => '
', + ); + $form['submit'][] = array('#type' => 'submit', '#value' => t('Insert')); + $form['submit'][] = array( + '#type' => 'markup', + '#value' => l(t('Cancel'), 'external', array('attributes' => array('class' => 'form-cancel'))), + ); + return $form; +} + Index: plugins/inline_node/inline_node.css =================================================================== RCS file: plugins/inline_node/inline_node.css diff -N plugins/inline_node/inline_node.css --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/inline_node/inline_node.css 14 Oct 2008 02:04:27 -0000 @@ -0,0 +1,10 @@ +/* $Id: drupalimage.css,v 1.1.4.1.2.1 2008/07/18 00:14:51 sun Exp $ */ + +.mceItemDrupalImage { + border: 1px dotted #cc0000; + background-image: url('images/img_assist.gif'); + background-position: center; + background-repeat: no-repeat; + background-color: #ffffcc; +} + Index: plugins/inline_node/inline_node.js =================================================================== RCS file: plugins/inline_node/inline_node.js diff -N plugins/inline_node/inline_node.js --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ plugins/inline_node/inline_node.js 27 Jan 2009 00:16:14 -0000 @@ -0,0 +1,97 @@ +// $Id: editor_plugin.js 201 2008-02-12 15:56:56Z sun $ + +Drupal.wysiwyg.plugins.inline_node = { + + // Return whether the passed node belongs to this plugin. + isNode: function (node) { + return ($(node).is('img.img-assist')); + }, + + // Execute the button. + invoke: function (data, settings, instanceId) { + if (data.format == 'html') { + // captionTitle and captionDesc for backwards compatibility. + var options = {nid: '', title: '', captionTitle: '', desc: '', captionDesc: '', link: '', url: '', align: '', width: '', height: '', id: instanceId, action: 'insert'}; + if ($(data.node).is('img.img-assist')) { + options.width = node.width; + options.height = node.height; + options.align = node.align; + // Expand inline tag in alt attribute + node.alt = decodeURIComponent(node.alt); + var chunks = node.alt.split('|'); + for (var i in chunks) { + chunks[i].replace(/([^=]+)=(.*)/g, function(o, property, value) { + options[property] = value; + }); + } + options.captionTitle = options.title; + options.captionDesc = options.desc; + options.action = 'update'; + } + } + else { + // @todo Plain text support. + /* + if (data.content.match(//)) { + return; + } + var content = ''; + */ + } + if (typeof options != 'undefined') { + Drupal.wysiwyg.instances[instanceId].openDialog(settings.dialog, options); + } + }, + + // Replace inline tags in data.content with images. + attach: function (content, settings, instanceId) { + content = content.replace(/\[img_assist\|([^\[\]]+)\]/g, function(orig, match) { + var node = {}, chunks = match.split('|'); + for (var i in chunks) { + chunks[i].replace(/([^=]+)=(.*)/g, function(o, property, value) { + node[property] = value; + }); + } + node.name = 'mceItemDrupalImage'; + node.src = Drupal.settings.basePath + 'index.php?q=image/view/' + node.nid; + node.alt = 'nid=' + node.nid + '|title=' + node.title + '|desc=' + node.desc; + if (node.link.indexOf(',') != -1) { + var link = node.link.split(',', 2); + node.alt += '|link=' + link[0] + '|url=' + link[1]; + } + else { + node.alt += '|link=' + node.link; + } + if (typeof node.url != 'undefined') { + node.alt += '|url=' + node.url; + } + node.alt = encodeURIComponent(node.alt); + element = document.createElement('IMG'); + for (property in node) { + element.property = node.property; + } + return element; + }); + return content; + }, + + // Replace images with inline tags in editor contents upon data.save. + detach: function (content, settings, instanceId) { + $content = $('
' + content + '
'); // No .outerHTML() in jQuery :( + console.log($content); + $('img', $content).each(function(node) { +// if (this.name != 'mceItemDrupalImage') { +// return; +// } + var inlineTag = '[img_assist|' + decodeURIComponent(this.alt) + '|align=' + this.align + '|width=' + this.width + '|height=' + this.height + ']'; + $(this).replaceWith(inlineTag); + }); + console.log($content); + return $content.html(); + }, + + insert: function (settings) { + return '[node|nid=' + settings.nid + ']'; + return '<--break->'; + } +}; Index: plugins/inline_node/images/inline_node.gif =================================================================== RCS file: plugins/inline_node/images/inline_node.gif diff -N plugins/inline_node/images/inline_node.gif Binary files /dev/null and inline_node.gif differ