eads@0: array( eads@0: 'arguments' => array('element' => NULL), eads@0: ), franck@11: 'mee_ressource_manager' => array( franck@11: 'arguments' => array('element' => NULL), franck@11: ), eads@0: 'mee_formatter_default' => array( eads@0: 'arguments' => array('element' => NULL), eads@0: ), franck@16: 'mee_formatter_plain' => array( franck@16: 'arguments' => array('element' => NULL), franck@16: ), franck@16: 'mee_formatter_short' => array( franck@16: 'arguments' => array('element' => NULL), franck@16: ), eads@0: ); tom@2: tom@2: $scald_config = variable_get('scald_config', 0); franck@14: if(!empty($scald_config->contexts)) { franck@14: foreach ($scald_config->contexts as $context => $details) { franck@14: $theme['mee_formatter_'. $context] = array( franck@14: 'arguments' => array('element' => NULL), franck@14: 'function' => 'theme_mee_context_formatter', franck@14: ); franck@14: } tom@2: } eads@0: return $theme; eads@0: } eads@0: eads@0: /** eads@0: * Implementation of hook_field_info(). eads@0: */ eads@0: function mee_field_info() { eads@0: return array( eads@0: 'multimedia_editorial_element' => array( eads@0: 'label' => t('Multimedia Editorial Element (MEE)'), eads@0: 'description' => t('MEE combines Scald, WYSIWYG, and DnD to create a multimedia enabled text field.'), eads@0: ), eads@0: ); eads@0: } eads@0: eads@0: /** eads@0: * Implementation of hook_field_settings(). eads@0: */ eads@0: function mee_field_settings($op, $field) { eads@0: switch ($op) { eads@0: case 'form': eads@0: $form = array(); eads@0: $options = array(0 => t('Plain text'), 1 => t('Filtered text (user selects input format)')); tom@5: tom@5: $scald_config = variable_get('scald_config', 0); tom@5: $context_options = array(); tom@5: $contexts_result = db_query("SELECT context, title FROM {scald_contexts} WHERE context IN ('" . implode("', '", array_keys($scald_config->contexts)) . "') ORDER BY title"); tom@5: while ($context_raw = db_fetch_array($contexts_result)) { tom@5: $context_options[$context_raw['context']] = $context_raw['title']; tom@5: } tom@5: eads@0: $form['mee_processing'] = array( eads@0: '#type' => 'radios', eads@0: '#title' => t('Text processing'), eads@0: '#default_value' => is_numeric($field['mee_processing']) ? $field['mee_processing'] : 1, eads@0: '#options' => $options, eads@0: '#description' => t('Filtered text, with a WYSIWYG editor defined on one or more input formats, is strongly recommended.'), eads@0: ); eads@0: // @TODO Ask Drupal about available libraries eads@0: $form['mee_dnd_callback_url'] = array( eads@0: '#type' => 'textfield', eads@0: '#title' => t('Library callback URL'), eads@0: '#default_value' => url($field['mee_dnd_callback_url']) ? $field['mee_dnd_callback_url'] : '', eads@0: '#description' => t('The absolute URL or relative path of a callback URL that provides proper JSON to the drag and drop library.'), eads@0: ); tom@5: $form['mee_scald_editor_context'] = array( tom@5: '#type' => 'select', tom@5: '#title' => t('Scald Editor Context'), tom@5: '#description' => t('Choose a Scald Context to use for displaying Scald Atoms included in the textarea during editing.'), tom@5: '#default_value' => $field['mee_scald_editor_context'], tom@5: '#options' => $context_options, tom@5: ); eads@0: return $form; eads@0: eads@0: case 'save': tom@4: return array('mee_processing', 'mee_dnd_callback_url', 'mee_scald_editor_context'); eads@0: eads@0: case 'database columns': eads@0: $columns['value'] = array('type' => 'text', 'size' => 'big', 'not null' => FALSE, 'sortable' => TRUE); franck@13: $columns['short'] = array('type' => 'text', 'size' => 'big', 'not null' => FALSE, 'sortable' => TRUE); eads@0: $columns['dnd_callback_url'] = array('type' => 'text', 'size' => 'small', 'not null' => FALSE); eads@0: if (!empty($field['mee_processing'])) { eads@0: $columns['format'] = array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE); eads@0: } tom@5: $columns['mee_scald_editor_context'] = array('type' => 'text', 'size' => 'small', 'not null' => FALSE); eads@0: return $columns; eads@0: eads@0: case 'views data': eads@0: return content_views_field_views_data($field); eads@0: } eads@0: } eads@0: eads@0: /** eads@0: * Implementation of hook_field(). eads@0: */ eads@0: function mee_field($op, &$node, $field, &$items, $teaser, $page) { eads@0: switch ($op) { tom@2: case 'presave': tom@5: foreach ($items as $delta => &$item) { franck@14: // Put everything in the ['mee'] namespace back into the array. franck@14: // This let CCK store the value and short fields natively. franck@14: if (is_array($item['mee'])) { franck@14: foreach ($item['mee'] as $k => $v) { franck@14: $items[$delta][$k] = $v; franck@14: } franck@14: } franck@14: if (!empty($item['value']) && variable_get('mee_store_sas', FALSE)) { franck@14: $item['value'] = scald_rendered_to_sas($item['value']); tom@5: } tom@5: } tom@2: break; // end 'submit' tom@2: tom@2: case 'insert': tom@2: foreach ($items as $delta => $item) { franck@13: // Process the value and generate an atom franck@14: $sas = scald_rendered_to_sas($item['value']); franck@14: $scald_included = scald_included($sas); franck@14: $sids = array_unique($scald_included); tom@3: defr@24: if (variable_get('mee_register_composites', FALSE)) { defr@24: $temp_atom = new stdClass; defr@24: $temp_atom->type = 'composite'; defr@24: $temp_atom->provider = 'mee'; defr@24: $temp_atom->base_id = $node->nid . ':' . $delta; defr@24: $temp_atom->publisher = $node->uid; defr@24: $temp_atom->title = $node->title . ' - ' . $field['widget']['label'] . ' (#' . $delta . ')'; defr@24: $temp_atom->authors = array(scald_uid_to_aid($node->uid)); defr@24: $temp_atom->relationships = empty($scald_included) ? array() : array('includes' => $scald_included); defr@24: defr@24: scald_register_atom($temp_atom); defr@24: } franck@14: franck@13: // Ressource manager associations franck@14: if (empty($item['ressource_manager'])) { franck@14: _mee_load_ressources($node, $field, $item); franck@14: } franck@14: $separator = $item['ressource_manager'][0]['weight']; franck@14: foreach ($sids as $sid) { franck@14: $ressource = $item['ressource_manager'][$sid]; franck@14: $weight = $ressource['weight'] - $separator; franck@14: $required = $ressource['required']; franck@14: $query = "INSERT into {mee_ressources} (content_nid, atom_sid, field, weight, required) VALUES (%d, %d, '%s', %d, %d)"; franck@14: db_query($query, $node->nid, $sid, $field['field_name'], $weight, $required); franck@13: } tom@2: } tom@2: break; // end 'insert' tom@2: tom@2: case 'update': tom@2: foreach ($items as $delta => $item) { franck@13: // Process the value franck@14: $sas = scald_rendered_to_sas($item['value']); franck@14: $scald_included = scald_included($sas); franck@14: $sids = array_unique($scald_included); franck@14: franck@14: // Update ressources weight franck@14: // In fact, we'll delete all the associations and recreate afterwards franck@14: // the needed one, to be sure that new ressources are correctly franck@14: // registered, and that no longer used one are removed. franck@14: if (!is_array($item['ressource_manager'])) { franck@14: _mee_load_ressources($node, $field, $item); franck@14: } franck@14: db_query("DELETE FROM {mee_ressources} WHERE content_nid=%d AND field='%s'", $node->nid, $field['field_name']); franck@14: // We'll normalize the weight, putting our separator at 0. franck@14: $separator = $item['ressource_manager'][0]['weight']; franck@14: foreach ($sids as $sid) { franck@14: $ressource = $item['ressource_manager'][$sid]; franck@14: $required = $ressource['required']; franck@14: $weight = $ressource['weight'] - $separator; franck@14: // insert in the table franck@14: $query = "INSERT into {mee_ressources} (content_nid, atom_sid, field, weight, required) VALUES (%d, %d, '%s', %d, %d)"; franck@14: db_query($query, $node->nid, $sid, $field['field_name'], $weight, $required); franck@14: } tom@2: tom@3: // @@@TODO: Handle failure of fetch defr@25: if (variable_get('mee_register_composites', FALSE)) { defr@25: $atom = scald_fetch(scald_search(array('base_id' => $node->nid . ':' . $delta), FALSE, TRUE)); defr@25: $atom->publisher = $node->uid; defr@25: $atom->title = $node->title; defr@25: $atom->authors = array(scald_uid_to_aid($node->uid)); // @@@TODO: This will completely override any authors listed & replace only with the Publisher. defr@25: $atom->relationships = empty($scald_included) ? array() : array('includes' => $scald_included); tom@3: defr@25: scald_update_atom($atom); defr@25: } tom@2: } tom@2: break; // end 'update' tom@2: tom@2: case 'delete': tom@3: foreach ($items as $delta => $item) { tom@3: scald_unregister_atom(scald_search(array('base_id' => $node->nid . ':' . $delta), FALSE, TRUE)); tom@3: } franck@14: franck@14: // Delete all ressources associations for this field franck@14: $query = "DELETE FROM {mee_ressources} WHERE content_nid = %d AND field = '%s'"; franck@14: db_query($query, $node->nid, $field['field_name']); tom@2: break; // end 'delete' tom@2: eads@0: case 'sanitize': eads@0: foreach ($items as $delta => $item) { eads@0: if (!empty($field['mee_processing'])) { eads@0: $check = is_null($node) || (isset($node->build_mode) && $node->build_mode == NODE_BUILD_PREVIEW); franck@14: $text = isset($item['value']) ? check_markup($item['value'], $item['format'], $check) : ''; eads@0: } eads@0: else { franck@14: $text = check_plain($item['value']); eads@0: } eads@0: $items[$delta]['safe'] = $text; eads@0: } tom@2: break; // end 'sanitize' eads@0: } eads@0: } eads@0: eads@0: /** eads@0: * Implementation of hook_content_is_empty(). eads@0: */ eads@0: function mee_content_is_empty($item, $field) { franck@14: if (empty($item['value']) && (string)$item['value'] !== '0') { eads@0: return TRUE; eads@0: } eads@0: return FALSE; eads@0: } eads@0: eads@0: /** eads@0: * Implementation of hook_field_formatter_info(). eads@0: */ eads@0: function mee_field_formatter_info() { eads@0: $formatters = array( eads@0: 'default' => array( eads@0: 'label' => t('Filtered text'), eads@0: 'field types' => array('multimedia_editorial_element'), eads@0: 'multiple values' => CONTENT_HANDLE_CORE, eads@0: ), eads@0: 'plain' => array( eads@0: 'label' => t('Plain text'), eads@0: 'field types' => array('multimedia_editorial_element'), eads@0: 'multiple values' => CONTENT_HANDLE_CORE, eads@0: ), franck@16: 'short' => array( franck@16: 'label' => t('Short content'), franck@16: 'field types' => array('multimedia_editorial_element'), franck@16: 'multiple values' => CONTENT_HANDLE_CORE franck@16: ) eads@0: ); eads@0: //@TODO generate context processor based field formatters eads@0: //foreach (scald_contexts() as $context) { eads@0: // $formatters[$context] = array( eads@0: // 'label' => t('Scald context processor: @context', array('@context' => $context), eads@0: // 'field types' => 'mee', eads@0: // ); eads@0: //} eads@0: return $formatters; eads@0: } eads@0: eads@0: function theme_mee_formatter_default($element) { franck@14: // What's stored is exactly what the user entered, and not the SAS franck@14: // representation. In general, that's that we want to output, *but* franck@14: // we should also check that the atom is still available... And replace franck@14: // it if it isn't. franck@14: if (!variable_get('mee_store_sas', FALSE)) { franck@14: $sas = scald_rendered_to_sas($element['#item']['value']); franck@14: $included = scald_included($sas); franck@14: $altered = FALSE; franck@14: foreach($included as $sid) { franck@14: $atom = scald_fetch($sid); franck@14: if (!scald_action_permitted($atom, 'view')) { franck@14: $altered = TRUE; franck@14: $replace = scald_scald_render($atom, 'no-access'); franck@14: $element['#item']['value'] = preg_replace( franck@14: "//sU", franck@14: scald_scald_render($atom, 'no-access'), franck@14: $element['#item']['value'] franck@14: ); franck@14: } franck@14: } franck@14: if ($altered) { franck@14: $element['#item']['safe'] = check_markup($element['#item']['value']); franck@14: } franck@14: } tom@4: return scald_sas_to_rendered($element['#item']['safe']); eads@0: } eads@0: eads@0: /** eads@0: * Theme function for 'plain' text field formatter. eads@0: */ eads@0: function theme_mee_formatter_plain($element) { tom@4: return strip_tags(scald_sas_to_rendered($element['#item']['safe'], 'title', TRUE)); eads@0: } eads@0: franck@16: /** franck@16: * Theme function for 'short' text field formatter. franck@16: */ franck@16: function theme_mee_formatter_short($element) { defr@23: $value = $element['#item']['short']; defr@23: return empty($value) ? $value : check_markup($value); franck@16: } franck@16: tom@4: //function theme_mee_context_formatter($element) { tom@4: // return 'foo'; tom@4: //} eads@0: eads@0: /** eads@0: * Implementation of hook_widget_info(). eads@0: */ eads@0: function mee_widget_info() { eads@0: return array( eads@0: 'mee_textarea' => array( eads@0: 'label' => t('MEE Textarea'), eads@0: 'field types' => array('multimedia_editorial_element'), eads@0: 'multiple values' => CONTENT_HANDLE_CORE, eads@0: ), eads@0: ); eads@0: } eads@0: eads@0: /** eads@0: * Implementation of FAPI hook_elements(). eads@0: * eads@0: * Any FAPI callbacks needed for individual widgets can be declared here, eads@0: * and the element will be passed to those callbacks for processing. eads@0: * eads@0: * Drupal will automatically theme the element using a theme with eads@0: * the same name as the hook_elements key. eads@0: */ eads@0: function mee_elements() { eads@0: return array( eads@0: 'mee_textarea' => array( eads@0: '#input' => TRUE, eads@0: '#columns' => array('value', 'format'), '#delta' => 0, eads@0: '#process' => array('mee_textarea_process', 'dnd_process_textarea'), eads@0: '#filter_value' => FILTER_FORMAT_DEFAULT, eads@0: ), eads@0: ); eads@0: } eads@0: eads@0: /** eads@0: * Implementation of hook_widget_settings(). eads@0: */ eads@0: function mee_widget_settings($op, $widget) { eads@0: switch ($op) { eads@0: case 'form': eads@0: $form = array(); eads@0: $rows = (isset($widget['rows']) && is_numeric($widget['rows'])) ? $widget['rows'] : 5; eads@0: $size = (isset($widget['size']) && is_numeric($widget['size'])) ? $widget['size'] : 60; eads@0: $form['rows'] = array( eads@0: '#type' => 'textfield', eads@0: '#title' => t('Rows'), eads@0: '#default_value' => $rows, eads@0: '#element_validate' => array('_mee_widget_settings_row_validate'), eads@0: '#required' => TRUE, eads@0: ); eads@0: $form['size'] = array('#type' => 'hidden', '#value' => $size); eads@0: return $form; eads@0: eads@0: case 'save': eads@0: return array('rows', 'size'); eads@0: } eads@0: } eads@0: eads@0: function _mee_widget_settings_row_validate($element, &$form_state) { eads@0: $value = $form_state['values']['rows']; eads@0: if (!is_numeric($value) || intval($value) != $value || $value <= 0) { eads@0: form_error($element, t('"Rows" must be a positive integer.')); eads@0: } eads@0: } eads@0: eads@0: function _mee_widget_settings_size_validate($element, &$form_state) { eads@0: $value = $form_state['values']['size']; eads@0: if (!is_numeric($value) || intval($value) != $value || $value <= 0) { eads@0: form_error($element, t('"Size" must be a positive integer.')); eads@0: } eads@0: } eads@0: eads@0: /** eads@0: * Implementation of hook_widget(). eads@0: * eads@0: * Attach a single form element to the form. It will be built out and eads@0: * validated in the callback(s) listed in hook_elements. We build it eads@0: * out in the callbacks rather than here in hook_widget so it can be eads@0: * plugged into any module that can provide it with valid eads@0: * $field information. eads@0: * eads@0: * Content module will set the weight, field name and delta values eads@0: * for each form element. This is a change from earlier CCK versions eads@0: * where the widget managed its own multiple values. eads@0: * eads@0: * If there are multiple values for this field, the content module will eads@0: * call this function as many times as needed. eads@0: * eads@0: * @param $form eads@0: * the entire form array, $form['#node'] holds node information eads@0: * @param $form_state eads@0: * the form_state, $form_state['values'][$field['field_name']] eads@0: * holds the field's form values. eads@0: * @param $field eads@0: * the field array eads@0: * @param $items eads@0: * array of default values for this field eads@0: * @param $delta eads@0: * the order of this item in the array of subelements (0, 1, 2, etc) eads@0: * eads@0: * @return eads@0: * the form item for a single element for this field eads@0: */ eads@0: function mee_widget(&$form, &$form_state, $field, $items, $delta = 0) { tom@5: if (isset($items[$delta]['value'])) { tom@5: $items[$delta]['value'] = scald_sas_to_rendered($items[$delta]['value'], $field['mee_scald_editor_context'], TRUE); tom@5: } eads@0: $element = array( eads@0: '#type' => $field['widget']['type'], tom@5: '#default_value' => isset($items[$delta]) ? $items[$delta] : '', eads@0: ); tom@5: eads@0: return $element; eads@0: } eads@0: eads@0: /** eads@0: * Process an individual element. eads@0: * eads@0: * Build the form element. When creating a form using FAPI #process, eads@0: * note that $element['#value'] is already set. eads@0: * eads@0: * The $fields array is in $form['#field_info'][$element['#field_name']]. eads@0: */ eads@0: function mee_textarea_process($element, $edit, $form_state, $form) { eads@0: drupal_add_css(drupal_get_path('module', 'mee') .'/css/mee.css'); franck@8: drupal_add_js(drupal_get_path('module', 'mee') .'/mee.js'); franck@11: $element['mee'] = array( franck@11: '#type' => 'markup', franck@11: '#prefix' => '