editablefields.module in Editable Fields 6.3
Editable fields module.
File
editablefields.moduleView source
<?php
/**
* @file
* Editable fields module.
*/
/**
* Implementation of hook_perm.
*
* @return array
* An array of permissions that this module provides.
*/
function editablefields_perm() {
return array(
'administer editablefields',
);
}
/**
* Implementation of hook_ctools_plugin_directory().
*
* @param $module
* @param $plugin_type
* @return string
*/
function editablefields_ctools_plugin_directory($module, $plugin_type) {
if ($plugin_type == 'export_ui') {
return "plugins/export_ui";
}
if ($module == 'editablefields' && !empty($plugin_type)) {
return "plugins/{$plugin_type}";
}
}
/**
* Implementation of hook_ctools_plugin_api().
*
* Tell Ctools that we support the default_editablefields_presets API.
*/
function editablefields_ctools_plugin_api($owner, $api) {
if ($owner == 'editablefields' && $api == 'default_editablefields_presets') {
return array(
'version' => 1,
);
}
}
/**
* Implementation of hook_default_editablefields_preset().
*
* Provide a couple of default presets.
*/
function editablefields_default_editablefields_preset() {
$export = array();
$export['modal'] = (object) array(
'api_version' => 1,
'name' => 'modal',
'description' => 'Display the field form in a Modal Dialog',
'config' => array(
'editable_type' => 'modal',
),
);
$export['html'] = (object) array(
'api_version' => 1,
'name' => 'html',
'description' => 'Display the field form inline',
'config' => array(
'editable_type' => 'html',
),
);
return $export;
}
/**
* Implementation of hook_menu_alter().
*/
function editablefields_menu_alter(&$items) {
// takeover the filefield ahah callback. This is a temporary hackish solution.
if (isset($items['filefield/ahah/%/%/%'])) {
$items['filefield/ahah/%/%/%'] = array(
'page callback' => 'editablefields_filefield_js',
) + $items['filefield/ahah/%/%/%'];
}
}
/**
* Implementation of hook_menu().
*/
function editablefields_menu() {
$items = array();
// Admin pages:
$items['editablefields_view'] = array(
'page callback' => 'editablefields_view',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
'title' => 'ajax view',
);
$items['editablefields_html'] = array(
'page callback' => 'editablefields_html',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
'title' => 'ajax form',
);
$items['editablefields_submit'] = array(
'page callback' => 'editablefields_submit',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
'title' => 'ajax submit',
);
$plugins = editablefields_get_responders();
foreach ($plugins as $plugin_name => $plugin) {
foreach ($plugin['hook_menu'] as $plugin_item_path => $plugin_item) {
$items[$plugin_item_path] = $plugin_item + array(
'title' => $plugin['title'],
'file' => $plugin['file'],
'file path' => $plugin['path'],
'type' => MENU_CALLBACK,
);
}
}
return $items;
}
/**
* Implementation of hook_theme().
*/
function editablefields_theme() {
$items = array();
$plugins = editablefields_get_editables();
foreach ($plugins as $plugin_name => $plugin) {
if (!empty($plugin['theme'])) {
$items = array_merge($items, $plugin['theme']);
}
}
return $items;
}
/*
function editablefields_theme_registry_alter(&$items) {
$items['views_view_field']['function'] = 'editablefields_views_view_field';
}
*/
/**
* Implementation of hook_field_formatter_info().
*/
function editablefields_field_formatter_info() {
$items = array();
$plugins = editablefields_get_editables();
foreach ($plugins as $plugin_name => $plugin) {
if (!empty($plugin['formatter_info'])) {
$items = array_merge($items, $plugin['formatter_info']);
}
}
return $items;
}
/**
* Implementation of hook_content_build_modes().
*
* @return array
*/
function editablefields_content_build_modes() {
// Tell CCK about our "build" modes so that users can specify
// how they want the text in their modal/other link to be formatted
// on the Display Fields tab of the CCK admin.
$build_modes = array();
$plugins = editablefields_get_responders();
if (!empty($plugins)) {
$build_modes = array(
'editablefields' => array(
'title' => t('Editable'),
'build modes' => array(),
),
);
foreach ($plugins as $plugin_name => $plugin) {
foreach ($plugin['build modes'] as $mode_name => $mode_info) {
$build_modes['editablefields']['build modes'][$mode_name] = $mode_info;
}
}
}
return $build_modes;
}
/**
* Implementation of hook_form_alter().
*/
function editablefields_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'content_display_overview_form') {
// Remove editable field formatters from the options.
if ($form['#contexts'] == 'editablefields') {
$formatters = editablefields_field_formatter_info();
foreach ($form as $key => &$element) {
if (substr($key, 0, 6) == 'field_') {
foreach ($element as $setting_name => &$setting) {
if (!empty($setting['format'])) {
foreach ($formatters as $formatter_name => $formatter) {
unset($setting['format']['#options'][$formatter_name]);
unset($setting['format']['base']['#options'][$formatter_name]);
}
}
}
}
}
}
}
}
/**
* Editablefields version of replace CTools command.
* It uses the
*
* The replace command will replace a portion of the current document
* with the specified HTML.
*
* @param $html
* The data to use with the jquery replace() function.
*
* @return array
*/
function ctools_ajax_command_replace_active_element($index, $html) {
return array(
'command' => 'replace_active_element',
'active_elem_index' => $index,
'data' => $html,
);
}
/**
* Theme the editable field.
*/
function theme_editablefields_formatter_editable($element) {
static $js_ready;
$field_name = $element['#field_name'];
$field = content_fields($field_name);
$revision = !empty($element['#node']->vid) ? $element['#node']->vid : NULL;
$node = node_load($element['#node']->nid, $revision);
$delta = $element['#item']['#delta'];
// to disable overriding textareas just set this variable to 0
if (variable_get('editablefields_textareas_resizable_threshold', 25)) {
static $textareas_count = 0;
if ($field['type'] == 'text' && $field['widget']['type'] == 'text_textarea') {
if ($textareas_count++ >= variable_get('editablefields_textareas_resizable_threshold', 25)) {
drupal_add_js(drupal_get_path('module', 'editablefields') . '/editablefields_overrides.js');
}
}
}
// See if access to this form element is restricted,
// if so, skip widget processing and just set the value.
if (!node_access('update', $node) || !content_access('edit', $field)) {
// can't edit
$formatter_name = 'default';
if ($formatter = _content_get_formatter($formatter_name, $field['type'])) {
$theme = $formatter['module'] . '_formatter_' . $formatter_name;
return theme($theme, $element);
}
}
else {
$formatter_name = 'default';
if ($formatter = _content_get_formatter($formatter_name, $field['type'])) {
if (!isset($js_ready)) {
$js_ready = TRUE;
drupal_add_js('misc/jquery.form.js');
drupal_add_js(drupal_get_path('module', 'editablefields') . '/editablefields.js');
drupal_add_css(drupal_get_path('module', 'editablefields') . '/editablefields.css');
$settings = array(
'url_html' => url('editablefields_html', array(
'absolute' => TRUE,
)),
'url_submit' => url('editablefields_submit', array(
'absolute' => TRUE,
)),
'url_view' => url('editablefields_view', array(
'absolute' => TRUE,
)),
'clicktoedit_message' => theme('editablefields_clicktoedit_message'),
'clicktoedit_message_empty' => theme('editablefields_clicktoedit_message_empty'),
);
drupal_add_js(array(
'editablefields' => $settings,
), 'setting');
}
// [andreiashu] not sure how this works exactly but it does...
if (content_handle('widget', 'multiple values', $field) != CONTENT_HANDLE_CORE) {
if ($delta != 0) {
return;
}
}
$theme = $formatter['module'] . '_formatter_' . $formatter_name;
$class = "editablefields";
if ($element['#formatter'] == 'editable') {
$class .= " ajax-editable";
}
elseif ($element['#formatter'] == 'clicktoedit') {
$class .= " clicktoedit";
}
elseif ($element['#formatter'] == 'editable_html') {
$class .= " editablefields-html-load";
}
$pre = '<div class="' . $class . '" nid="' . $node->nid . '" field="' . $field_name . '" delta="' . $delta . '">';
$post = '</div>';
if ($element['#formatter'] != 'editable_html') {
$themed_element = theme($theme, $element);
// add the edit link for clicktoedit formatters
if ($element['#formatter'] == 'clicktoedit') {
if (!empty($themed_element)) {
$pre .= theme('editablefields_clicktoedit_message');
}
else {
$pre .= theme('editablefields_clicktoedit_message_empty');
}
}
return $pre . $themed_element . $post;
}
else {
// $node seems to be incomplete, so we reload it
$node = node_load($node->nid, $revision);
return $pre . drupal_get_form('editablefields_form', $node, $field_name, $delta) . $post;
}
}
}
}
function theme_editablefields_clicktoedit_message() {
return '<span class="editablefields_clicktoedit_message editablefields-hide">' . t('[edit]') . '</span>';
}
function theme_editablefields_clicktoedit_message_empty() {
return '<span class="editablefields_clicktoedit_message">' . t('[edit]') . '</span>';
}
/**
* Implementation of hook_forms().
*/
function editablefields_forms() {
$forms = array();
$forms['editablefields_form'] = array(
'callback' => 'editablefields_form_builder',
);
return $forms;
}
/**
* Form builder callback.
*/
function editablefields_form_builder(&$form_state, $node, $field_name, $delta) {
$field = content_fields($field_name);
$form = array(
'#node' => $node,
);
// $form_state = array('values' => array($field['field_name'] => $default_value));
module_load_include('inc', 'content', 'includes/content.node_form');
$form['#field_info'] = array(
$field['field_name'] => $field,
);
$form = content_field_form($form, $form_state, $field, $delta);
unset($form[$field_name]['#title']);
if (is_array($form[$field_name][0]) && !is_array($form[$field_name][1])) {
unset($form[$field_name][0]['#title']);
}
$form['editablefields_node_nid'] = array(
'#type' => 'hidden',
'#value' => $node->nid,
);
$form['#field_info'] = array(
$field['field_name'] => $field,
);
$form['#pre_render'] = array(
'_editablefields_pre_render',
);
return $form;
}
/**
* Resizable Textarea has an issue with setting focus.
* To circumvent this we have to set the forms fields #resizable field to FALSE.
* We have to use pre_render because the here the field's #type is still textarea.
* TODO Keep editable_HTML and editable_JAVA fields resizable, they don't have the focus issue.
*/
function _editablefields_pre_render(&$form) {
if ($form['field_name'][0]['value']['#type'] == 'textarea') {
$form['field_name'][0]['value']['#resizable'] = FALSE;
}
return $form;
}
/**
* Menu callback: ajax view.
*/
function editablefields_view() {
$output = '';
$nid = arg(1);
$field_name = arg(2);
$delta = arg(3);
$node = node_load($nid);
drupal_set_header('Content-Type: text; charset=utf-8');
// $html = node_view($node, FALSE, FALSE, FALSE);
// this required traversing the entire node to get the field (and doesn't work
// for checkboxes anyway)
$field = content_fields($field_name, $node->type);
$field['display_settings']['label']['format'] = 'hidden';
// We have 2 reasonable choices here. We COULD use 'clicktoedit' and end up
// with the same HTML - then we could strip that HTML down to remove the
// surrounding 'click to edit' div (which we dont want, as we'll be replacing
// the html inside one of those div's)
// or we can simply use the 'defualt' formatter - which wont have the click to
// edit inside it - and is hard coded into this module (for now) anyway!
$field['display_settings']['full']['format'] = 'default';
$html = content_view_field($field, $node);
$messages = drupal_get_messages('status', TRUE);
if (count($messages) > 0) {
foreach ($messages as $type => $messages) {
foreach ($messages as $message) {
$output .= '<div class="messages ' . $type . '">' . $message . "</div>";
}
}
}
$object = new stdClass();
$object->content = $output . $html . drupal_render($node->content[$field_name]);
drupal_json($object);
exit;
}
/**
* Menu callback: ajax form.
*/
function editablefields_html() {
$nid = arg(1);
$field_name = arg(2);
$delta = arg(3);
$node = node_load($nid);
if (node_access('update', $node)) {
// $html = _editablefields_create_form($node, $field_name);
$html = drupal_get_form('editablefields_form', $node, $field_name, $delta);
$object = new stdClass();
$object->content = $html;
// Register the JavaScript callback for this module.
$object->__callbacks = array();
// Allow other modules to extend the data returned.
drupal_alter('ajax_data', $object, 'editablefields', $html);
drupal_json($object);
}
else {
drupal_not_found();
}
exit;
}
/**
* Menu callback: ajax submit.
*/
function editablefields_submit() {
$nid = $_POST['nid'];
$field_name = $_POST['field'];
$delta = $_POST['delta'];
$node = node_load($nid);
$node_options = variable_get('node_options_' . $node->type, array(
'status',
'promote',
));
$node->revision = in_array('revision', $node_options);
if ($node->revision) {
$node->log = t('%field_name updated by editablefields.', array(
'%field_name' => $field_name,
));
}
if (node_access('update', $node)) {
if (!isset($_POST[$field_name])) {
$_POST[$field_name] = array();
}
$form_state = array(
'values' => $_POST,
);
/* it seems that the serializer does not serialize e.g. un-checked
* checkboxes. Leaving them as empty arrays. This FILTHY hack fills in the
* array with 'something' so that when the form is executed, it fills in the
* right value - I dislike this code - JMB */
if (is_array($node->{$field_name})) {
$field = content_fields($field_name, $node->type);
$items =& $form_state['values'][$field_name];
if (empty($items)) {
foreach (array_keys($field['columns']) as $column) {
if ($field['multiple']) {
$items[$delta][$column][] = NULL;
}
else {
$items[$column] = NULL;
}
}
}
if (isset($items['value'])) {
if (!($field['widget']['type'] == 'optionwidgets_buttons' && $field['multiple'])) {
$items = array(
$items,
);
}
}
// go through content_set_empty if this is NOT a checkbox multi valued element
if (!($field['widget']['type'] == 'optionwidgets_buttons' && $field['multiple'])) {
$items = content_set_empty($field, $items);
}
switch ($field['type']) {
case 'nodereference':
case 'userreference':
if ($field['multiple']) {
reset($field['columns']);
$items[key($field['columns'])] = array_pop($items);
}
break;
}
drupal_execute('editablefields_form', $form_state, $node, $field_name, $delta);
$err = drupal_get_messages();
if (count($err) > 0) {
drupal_set_header('HTTP/1.1 404 Not Found');
// format the error message suitable for a popup window in simple text.
foreach ($err as $type => $messages) {
foreach ($messages as $message) {
print $type . ' : ' . $message . "\n";
}
}
exit;
}
// the matrix field identifies itself as being multivalue, but in fact, it is not.
if (content_handle('widget', 'multiple values', $field) == CONTENT_HANDLE_CORE && $field['type'] != matrix) {
if ($node->{$field_name}[$delta] != $form_state['values'][$field_name][0]) {
$node->{$field_name}[$delta] = $form_state['values'][$field_name][0];
node_save($node);
}
}
else {
if ($node->{$field_name} != $form_state['values'][$field_name]) {
$node->{$field_name} = $form_state['values'][$field_name];
node_save($node);
}
}
// make sure sensible headers etc are sent...
drupal_set_header('Content-Type: text; charset=utf-8');
}
else {
drupal_set_header('HTTP/1.1 404 Not Found');
print t('No field found, of name: %field', array(
'%field' => $field_name,
));
}
}
else {
drupal_set_header('HTTP/1.1 404 Not Found');
print t('No write permissions for %field', array(
'%field' => $field_name,
));
}
exit;
}
/**
* If the Remove button was clicked remove the file from the node.
*/
function editablefields_filefield_js($type_name, $field_name, $delta) {
if (!empty($_POST['editablefields_node_nid']) && ($node = node_load($_POST['editablefields_node_nid']))) {
$remove_button_clicked = FALSE;
foreach ($_POST as $key => $value) {
// check if the remove button was clicked
if (stripos($key, $field_name) === 0 && stripos($key, '_filefield_remove') !== FALSE) {
$remove_button_clicked = TRUE;
break;
}
}
if ($remove_button_clicked) {
$field = $_POST[$field_name];
$field = array_pop($field);
$file = (object) field_file_load($field['fid']);
// respect revision settings
$node_options = variable_get('node_options_' . $node->type, array(
'status',
'promote',
));
$node->revision = in_array('revision', $node_options);
if ($node->revision) {
$node->log = t('%field_name updated by editablefields.', array(
'%field_name' => $field_name,
));
}
$file->field_name = $field_name;
$references = field_file_delete($file);
$node->{$field_name}[$delta] = array();
node_save($node);
drupal_set_message(t('File %filename was removed from node.', array(
'%filename' => $file->filename,
)));
}
}
filefield_js($type_name, $field_name, $delta);
}
/**
* Implementation of hook_file_insert().
*/
function editablefields_file_insert($file) {
if (stripos($_GET['q'], 'filefield/ahah/') === 0 && !empty($_POST['editablefields_node_nid']) && ($node = node_load($_POST['editablefields_node_nid']))) {
$file_clone = (array) $file;
field_file_save($node, $file_clone);
$q_arr = explode('/', $_GET['q']);
$field_name = $q_arr[3];
$delta = $q_arr[4];
$node->{$field_name}[$delta] = array(
'fid' => $file->fid,
);
// respect revision settings
$node_options = variable_get('node_options_' . $node->type, array(
'status',
'promote',
));
$node->revision = in_array('revision', $node_options);
if ($node->revision) {
$node->log = t('%field_name updated by editablefields.', array(
'%field_name' => $field_name,
));
}
node_save($node);
}
}
function editablefields_formatter_get_settings($field_name, $type_name, $context) {
$options = array();
$value = 'editablefields:' . $type_name . ':' . $context . ':' . $field_name;
$options['editablefields'] = variable_get($value . '_text_formatter', 'default');
/*
$options['repeat']['show_repeat_rule'] = variable_get($value .'_show_repeat_rule', 'show');
$options['multiple']['multiple_number'] = variable_get($value .'_multiple_number', '');
$options['multiple']['multiple_from'] = variable_get($value .'_multiple_from', '');
$options['multiple']['multiple_to'] = variable_get($value .'_multiple_to', '');
$options['fromto']['fromto'] = variable_get($value .'_fromto', 'both')
*/
return $options;
}
/**
* Returns all 'editables' plugins.
*
* @return array
*/
function editablefields_get_responders() {
ctools_include('plugins');
$plugins = ctools_get_plugins('editablefields', 'responders');
foreach ($plugins as &$plugin) {
// add defaults
$plugin += array(
'hook_menu' => array(),
'formatter_info' => array(),
);
}
return $plugins;
}
function editablefields_get_editables() {
ctools_include('plugins');
$plugins = ctools_get_plugins('editablefields', 'editables');
return $plugins;
}
/**
* Menu wildcard argument callback.
*/
function editablefields_object_load($object_id, $editable) {
$editables = editablefields_get_editables();
$function = $editables[$editable]['entity callback'];
return $function($object_id);
}
/**
* Implementation of hook_views_api().
*/
function editablefields_views_api() {
return array(
'api' => 2,
'path' => drupal_get_path('module', 'editablefields') . '/includes/views',
);
}
function editablefields_views_option_form(&$form, &$form_state, $editable) {
$form['editablefields_allowed'] = array(
'#title' => t('Make field editable'),
'#description' => t('This will override any other link you have set.'),
'#type' => 'checkbox',
'#default_value' => '',
);
$responders = editablefields_get_responders();
$options = array();
foreach ($responders as $responder) {
$options[$responder['name']] = $responder['title'];
}
$form['editablefields_responder'] = array(
'#title' => t('Editable Behavior'),
'#description' => t('How would you like to edit this field?'),
'#type' => 'select',
'#options' => $options,
'#dependency' => array(
'edit-options-editablefields-allowed' => array(
'1',
),
),
'#process' => array(
'views_process_dependency',
),
);
$form['editablefields_editable'] = array(
'#type' => 'hidden',
'#value' => $editable,
);
}
Functions
Name | Description |
---|---|
ctools_ajax_command_replace_active_element | Editablefields version of replace CTools command. It uses the |
editablefields_content_build_modes | Implementation of hook_content_build_modes(). |
editablefields_ctools_plugin_api | Implementation of hook_ctools_plugin_api(). |
editablefields_ctools_plugin_directory | Implementation of hook_ctools_plugin_directory(). |
editablefields_default_editablefields_preset | Implementation of hook_default_editablefields_preset(). |
editablefields_field_formatter_info | Implementation of hook_field_formatter_info(). |
editablefields_filefield_js | If the Remove button was clicked remove the file from the node. |
editablefields_file_insert | Implementation of hook_file_insert(). |
editablefields_formatter_get_settings | |
editablefields_forms | Implementation of hook_forms(). |
editablefields_form_alter | Implementation of hook_form_alter(). |
editablefields_form_builder | Form builder callback. |
editablefields_get_editables | |
editablefields_get_responders | Returns all 'editables' plugins. |
editablefields_html | Menu callback: ajax form. |
editablefields_menu | Implementation of hook_menu(). |
editablefields_menu_alter | Implementation of hook_menu_alter(). |
editablefields_object_load | Menu wildcard argument callback. |
editablefields_perm | Implementation of hook_perm. |
editablefields_submit | Menu callback: ajax submit. |
editablefields_theme | Implementation of hook_theme(). |
editablefields_view | Menu callback: ajax view. |
editablefields_views_api | Implementation of hook_views_api(). |
editablefields_views_option_form | |
theme_editablefields_clicktoedit_message | |
theme_editablefields_clicktoedit_message_empty | |
theme_editablefields_formatter_editable | Theme the editable field. |
_editablefields_pre_render | Resizable Textarea has an issue with setting focus. To circumvent this we have to set the forms fields #resizable field to FALSE. We have to use pre_render because the here the field's #type is still textarea. TODO Keep editable_HTML and… |